From 9625fd163f0aa9f285d69b353988d9b7b0348388 Mon Sep 17 00:00:00 2001 From: Rei Date: Sat, 21 Jul 2018 21:27:20 -0400 Subject: [PATCH] init commit --- .gitignore | 3 + LICENSE.txt | 339 ++ Makefile | 54 + README.md | 32 + data/loader.kip | Bin 0 -> 152344 bytes data/sm.kip | Bin 0 -> 102512 bytes data/splash.bin | Bin 0 -> 3686400 bytes link.ld | 29 + src/bootloader.c | 196 + src/bootloader.h | 19 + src/bootrom.c | 40 + src/bootrom.h | 19 + src/error.c | 36 + src/error.h | 23 + src/firmware.c | 351 ++ src/firmware.h | 17 + src/fs.c | 116 + src/fs.h | 9 + src/fuse.c | 47 + src/fuse.h | 21 + src/hwinit.h | 59 + src/hwinit/ELF.h | 1667 ++++++++ src/hwinit/btn.c | 42 + src/hwinit/btn.h | 29 + src/hwinit/clock.c | 449 ++ src/hwinit/clock.h | 108 + src/hwinit/cluster.c | 127 + src/hwinit/cluster.h | 28 + src/hwinit/di.c | 173 + src/hwinit/di.h | 72 + src/hwinit/di.inl | 548 +++ src/hwinit/diskio.c | 71 + src/hwinit/diskio.h | 80 + src/hwinit/emc.h | 667 +++ src/hwinit/ff.c | 6555 +++++++++++++++++++++++++++++ src/hwinit/ff.h | 366 ++ src/hwinit/ffconf.h | 283 ++ src/hwinit/ffunicode.c | 608 +++ src/hwinit/gfx.c | 522 +++ src/hwinit/gfx.h | 78 + src/hwinit/gpio.c | 94 + src/hwinit/gpio.h | 77 + src/hwinit/heap.c | 142 + src/hwinit/heap.h | 28 + src/hwinit/i2c.c | 132 + src/hwinit/i2c.h | 35 + src/hwinit/ini.c | 141 + src/hwinit/ini.h | 42 + src/hwinit/integer.h | 38 + src/hwinit/kfuse.c | 42 + src/hwinit/kfuse.h | 41 + src/hwinit/list.h | 95 + src/hwinit/lz.c | 179 + src/hwinit/lz.h | 52 + src/hwinit/max77620.h | 324 ++ src/hwinit/max7762x.c | 141 + src/hwinit/max7762x.h | 68 + src/hwinit/mc.c | 136 + src/hwinit/mc.h | 13 + src/hwinit/mc_t210.h | 466 ++ src/hwinit/mmc.h | 432 ++ src/hwinit/nx_emmc.c | 76 + src/hwinit/nx_emmc.h | 72 + src/hwinit/pinmux.c | 32 + src/hwinit/pinmux.h | 87 + src/hwinit/pmc.h | 49 + src/hwinit/pmc_t210_lp0.h | 563 +++ src/hwinit/sd.h | 109 + src/hwinit/sdmmc.c | 1129 +++++ src/hwinit/sdmmc.h | 111 + src/hwinit/sdmmc_driver.c | 1092 +++++ src/hwinit/sdmmc_driver.h | 126 + src/hwinit/sdmmc_t210.h | 132 + src/hwinit/sdram.c | 522 +++ src/hwinit/sdram.h | 24 + src/hwinit/sdram.inl | 1152 +++++ src/hwinit/sdram_lp0.c | 1102 +++++ src/hwinit/sdram_lz.inl | 124 + src/hwinit/sdram_param_t210.h | 930 ++++ src/hwinit/sdram_param_t210_lp0.h | 961 +++++ src/hwinit/se.c | 256 ++ src/hwinit/se.h | 30 + src/hwinit/se_t210.h | 357 ++ src/hwinit/t210.h | 125 + src/hwinit/tsec.c | 136 + src/hwinit/tsec.h | 24 + src/hwinit/types.h | 56 + src/hwinit/uart.c | 78 + src/hwinit/uart.h | 59 + src/hwinit/util.c | 45 + src/hwinit/util.h | 33 + src/package.c | 143 + src/package.h | 90 + src/reloc.s | 34 + src/start.s | 90 + 95 files changed, 26550 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.md create mode 100644 data/loader.kip create mode 100644 data/sm.kip create mode 100644 data/splash.bin create mode 100644 link.ld create mode 100644 src/bootloader.c create mode 100644 src/bootloader.h create mode 100644 src/bootrom.c create mode 100644 src/bootrom.h create mode 100644 src/error.c create mode 100644 src/error.h create mode 100644 src/firmware.c create mode 100644 src/firmware.h create mode 100644 src/fs.c create mode 100644 src/fs.h create mode 100644 src/fuse.c create mode 100644 src/fuse.h create mode 100644 src/hwinit.h create mode 100644 src/hwinit/ELF.h create mode 100644 src/hwinit/btn.c create mode 100644 src/hwinit/btn.h create mode 100644 src/hwinit/clock.c create mode 100644 src/hwinit/clock.h create mode 100644 src/hwinit/cluster.c create mode 100644 src/hwinit/cluster.h create mode 100644 src/hwinit/di.c create mode 100644 src/hwinit/di.h create mode 100644 src/hwinit/di.inl create mode 100644 src/hwinit/diskio.c create mode 100644 src/hwinit/diskio.h create mode 100644 src/hwinit/emc.h create mode 100644 src/hwinit/ff.c create mode 100644 src/hwinit/ff.h create mode 100644 src/hwinit/ffconf.h create mode 100644 src/hwinit/ffunicode.c create mode 100644 src/hwinit/gfx.c create mode 100644 src/hwinit/gfx.h create mode 100644 src/hwinit/gpio.c create mode 100644 src/hwinit/gpio.h create mode 100644 src/hwinit/heap.c create mode 100644 src/hwinit/heap.h create mode 100644 src/hwinit/i2c.c create mode 100644 src/hwinit/i2c.h create mode 100644 src/hwinit/ini.c create mode 100644 src/hwinit/ini.h create mode 100644 src/hwinit/integer.h create mode 100644 src/hwinit/kfuse.c create mode 100644 src/hwinit/kfuse.h create mode 100644 src/hwinit/list.h create mode 100644 src/hwinit/lz.c create mode 100644 src/hwinit/lz.h create mode 100644 src/hwinit/max77620.h create mode 100644 src/hwinit/max7762x.c create mode 100644 src/hwinit/max7762x.h create mode 100644 src/hwinit/mc.c create mode 100644 src/hwinit/mc.h create mode 100644 src/hwinit/mc_t210.h create mode 100644 src/hwinit/mmc.h create mode 100644 src/hwinit/nx_emmc.c create mode 100644 src/hwinit/nx_emmc.h create mode 100644 src/hwinit/pinmux.c create mode 100644 src/hwinit/pinmux.h create mode 100644 src/hwinit/pmc.h create mode 100644 src/hwinit/pmc_t210_lp0.h create mode 100644 src/hwinit/sd.h create mode 100644 src/hwinit/sdmmc.c create mode 100644 src/hwinit/sdmmc.h create mode 100644 src/hwinit/sdmmc_driver.c create mode 100644 src/hwinit/sdmmc_driver.h create mode 100644 src/hwinit/sdmmc_t210.h create mode 100644 src/hwinit/sdram.c create mode 100644 src/hwinit/sdram.h create mode 100644 src/hwinit/sdram.inl create mode 100644 src/hwinit/sdram_lp0.c create mode 100644 src/hwinit/sdram_lz.inl create mode 100644 src/hwinit/sdram_param_t210.h create mode 100644 src/hwinit/sdram_param_t210_lp0.h create mode 100644 src/hwinit/se.c create mode 100644 src/hwinit/se.h create mode 100644 src/hwinit/se_t210.h create mode 100644 src/hwinit/t210.h create mode 100644 src/hwinit/tsec.c create mode 100644 src/hwinit/tsec.h create mode 100644 src/hwinit/types.h create mode 100644 src/hwinit/uart.c create mode 100644 src/hwinit/uart.h create mode 100644 src/hwinit/util.c create mode 100644 src/hwinit/util.h create mode 100644 src/package.c create mode 100644 src/package.h create mode 100644 src/reloc.s create mode 100644 src/start.s diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bddef98 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +out/ +*zip \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,339 @@ + 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. + + GNU GENERAL PUBLIC LICENSE + 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. + + + Copyright (C) + + 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. + + , 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 instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ed466cd --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +CC = $(DEVKITARM)/bin/arm-none-eabi-gcc +LD = $(DEVKITARM)/bin/arm-none-eabi-ld +OBJCOPY = $(DEVKITARM)/bin/arm-none-eabi-objcopy + +name := ReiNX + +dir_source := src +dir_data := data +dir_build := build +dir_out := out + +ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork +CFLAGS = $(ARCH) -Os -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11# -Wall +LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections + +objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ + $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ + $(call rwildcard, $(dir_source), *.s *.c))) + +define bin2o + bin2s $< | $(AS) -o $(@) +endef + +.PHONY: all +all: $(dir_out)/$(name).bin + +.PHONY: clean +clean: + @rm -rf $(dir_build) + @rm -rf $(dir_out) + +$(dir_out)/$(name).bin: $(dir_build)/$(name).elf + @mkdir -p "$(@D)" + @mkdir -p "$(dir_out)/ReiNX/sysmodules" + @cp -R $(dir_data)/*.kip $(dir_out)/ReiNX/sysmodules/ + @cp -R $(dir_data)/*.bin $(dir_out)/ReiNX/ + $(OBJCOPY) -S -O binary $< $@ + +$(dir_build)/$(name).elf: $(objects) + $(CC) $(LDFLAGS) -T link.ld $^ -o $@ + +$(dir_build)/%.o: $(dir_source)/%.c + @mkdir -p "$(@D)" + $(CC) $(CFLAGS) -c $< -o $@ + +$(dir_build)/%.o: $(dir_source)/%.s + @mkdir -p "$(@D)" + $(CC) $(CFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..848e396 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# ReiNX +[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + +*The modular switch custom firmware* + +**Usage:** + +Put `ReiNX` folder on the root of your switch's SD card and run `ReiNX.bin` with your favorite fusee launcher. + +**Compiling:** + +You'll need devkitpro with devkitA64 and run `make` + + +**Features:** + +* Loads all KIPs from `/ReiNX/sysmodules/` directory + +* Optional custom kernel/secmon/warmboot + +* Default kips with exefs redir from `/ReiNX/title/{tid}` + + +**Credits:** + + Naehrwert for hardware init code and generally being helpful! + + CTCaer and st4rk for their contribution to the hardware code aswell! + + SciresM for kernel processes! + + The community for your support! \ No newline at end of file diff --git a/data/loader.kip b/data/loader.kip new file mode 100644 index 0000000000000000000000000000000000000000..e4928715840298598855a22dbdaf8041232d9036 GIT binary patch literal 152344 zcmc$`e?U{${WtzT_eXxgl^fuSsFd75)KJrclD27=aTAOcTQ*SAmbTOch)OM+*wV=? zC81zz8BdLcwzg#kTRKC_CLQc!woyTgtLu0wTDtk!TmwqgI`$!8lqi_zTq3po?DKh^ z=b!KM&B?v@ocDR3bI-Z&`{SI~`+eRIM_+xviV;ju=(9MH*%pf64ObWju;V&0& z<{TuUbPvGy9u+h2mPbMQ0|3-K92b8c3ez$o@&Ai`xW);eg~Q4J5w20iNc`C`0I&ZT z*Ao7J^iu(^UnBf5*J560c^I+InDM#r*YWFQ)@|+8@}|F~5Q9{RL)W^g{J7yH(5pq#` z&31sMUK(K81HL{=3H)wL=mr2f@%QX#1|a?uwuO+jSVGANkUJDRwEp_jhrJ=mmc!nw zC5M}7#P%NcPOk4e5PS^!4qPqiI}qA}BiEnqJJ3HFGu|uTo%H!ktkPVLAasAS1DZhL zJjuaxMJq?-U}5>K!otvG4ph^7%2{4mUM``~R80_V=s~SDHwbUd9?}anYr$8u0vUjQ z4w}^Ywxs+#PyVYzyJU(<8C&c(|wbq`w0_qk+fsUWX=|0zy?1XIH!l*%X3D*lfe9L zDnse;1C;!Qg`LUX@lmdQJp;BQPsJMyp!fR-t;%E4e%O`48rUBhK$QcwhmI^al{Mxc{rRPS0Unve0RtAVJ~RG zz#2<-Kod{EK^~lp8$Acr&?i*;Q8i_H9^%VS)aNV9*~_{4RS+cc&FZ&9p99V+!LIt8 z0Up48uN_8ABsU82?XAkYtnjW$Y0DQfl=&ylr1#{jdJ6M~6Cm`53tn&~2%)10#sQ** zP$O%1UPOD=K+~E#p{a%d@VU1hb=w-*GAfg7*p=tkKn^R9ZPsNJNjkwHZc6qwQcv+0nzmuuvjD~D%-WpB>Z-?3M(xGc}jMlmgJJ=YOr=_I-<n35n$?GS%v>-fY_Fi2igl1sJu>-y2hA+zFoQ}A%xQk_^|B4Wbs+0xF znvwte18^fJ5%zZw6a@MLsNVl|C(v-&4h1vI!`=Q^!2mN5){nd=gpN!D9raHkRL||= z&NdUBd<7kzaqCr&xamEQC9a_3bex}*w9o5NCO2xNjgGR$(Z=AAs!`KF!8cm1jX~8z zjh41I+pe`;>z{~(aQ}puYz&G?F%}kgqy2<|C3<0zyRDy~fo^D}cJ#0C_pAFSno<9e z{)ua<{$l*tr-PDEHCj+>+QoEe>~9SId(T8Co7~t&x08)r14Z29aU&)B&Mx}k;axQT z@84x9-lb`$cjBiuht8>j3iDJ{yy#82`CZ?}PIL|uzSBRwZ5qc$YLab$A;y)UL&m0ei?mW)zEYf(- zC9Lu;$rGUJuocdp2p62CCeeo@B-M^URs`1O2|qcXXy5b%gf5gIaF=d`(9!3P!dn}6 zLa1cg0;m2s9e&+Un84sq1AQTPkuokjGbIvEID>~Bx2+HmA@;yT_pex$194rweP$=W%iKrw6Q65Bn$TQ_#ba0# z;%4R4KOtsmLih@tNP6)H{pJW4&dbyo_A2?k$c-E;&Tzq=2`;GK1%ad&h`XI9)vJhm zlq@NzU@F-Ihmv72RGE;W7{9dFAm}P3;L|bIS$B|jzp&XprgEl?vB!i^fZc3>{mIX{ z4%q+r(n+vi@J`6vU_PZ1Ld}b0Eck*HIX~^8m%^D0>&%COA~uY_G+)vnz0}tYhyfz+ zF6YUXMeVOalg!yBD~jy}4jc$=lz}oCAizVx{PjG$Z=sHL+A<)wHe|~Eu=flcc_I(o zjr{+}o#>M-E4oqxxf63Cv?=$*J?lE&nvx#l&w#QA4(S2PcAwJT-0E7XEFep8T_ z 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%0Di|r(bX^nAm(Z3*@MaJC3 zO?=Xx?&Eg=aU8$AFi9oLf_zrSvQZ@#@Qo~$4J0&b9ZJ3nh2ZGiT(*B`gF zTt~2wzBtf0e!T%co4MX!EvNRLfyh~Vq54&Wcl<2)}YFT9$rwTK7!_;UkV`| zpMuZO@^H-YH+eG*l&71Xd)d6j>lh~Yj6tE0DHnS+uUKGY-2~F{*Or&(Dm+`f@TX!S zw6$-wAMUz?0k8*!5QHOTY01Z(vkZsJE*0ML@!?cV9SG@K7;`A%4EV2}0NfnMVM-h# zB_YXcB7gl}Fo(n`2+2JQxsmHJL-u7%%O%;EXSWdEYPH)%2w_E=vESk~<7#rTwjoT{ zK2hRYN2M!^6B3u6-H;1inqM$#SC@3&VU2n$`dFKqzmNcjB@S|CzJ108_8?>8#v0Y- z3DHdX3jwY)0H-ZamEWV?<-BjL65eciy@-2=h3#_eLRg2vUEBnk@PEoov-?EQJrOIA zkT4Cujex@k8LDA}UQ?*&^m8-Io7!uj6Vq>UbiMqAbtI}&Q5=OV^#|Ja$dz)z1&qzKD8Z-D=!HQ^;l!py zWsH<o&rTdx6a@-eK{ za)>EqMm6_lwJ55GJ(jW#Ts{#LdaWYCD~P>tRSh3|HDa4r^Z7^&780De!*1SG4u`(b zeB8!Vp^x=fg!(*_GQ&Dg+X&Uv`R&$COnsGvw+gEoV6JA(A*NnfEF1q}ZbqN*T=s1Y zm6KcD+3vF+g_h5J1-gvp6tRIw@+pyOop9)5&F|Z;RwlVcCwCc0*`iv!6mwyoFe&cm zuH3>T{DD;i`6oRvzN0g&SMv?hRmNOCn7~Lfu=iC0uAIbjA(vo~7c}}8@g(sQjQ>8JsEQ48V%c_DiPx<2B4=9vOC2v~R zt!aNIsO0CQWkzlB!y#qji@gBoAC#<8>}1_|f3t;M2|FQ;xDYB8h^smLK;h0laJ8>i zaoKj%`HDC06i`<{Bb@gr%iY6*FTo^7<5x}5mZ*itK3I79P0F#x@K{H&IM^M&?jd=D ztS7vR=U3P2*}XqOonsi_uOS>Hjy;-vJNp)CDw{ak%D*1vo{&j;B8&#%Bp z(>8F-60PQ3n}wa#l36WdqQ?~V3LND1uZOTj^OWkr$sf1qmFaa!;9Vl$R zk+Rx)hjx*1>R1G#-vet;nI2z$2UUm50Y=+`K_1b?;=_+P#D^W?f#{9JVgdBgP4fvp zek9?BEpg_-zhkCZ`oukX@RD#WeqtX4wzfyO6W~~Us%T@CUvjV~9C~u1t7s2%kPv0T z^r}CZP^e*#+ze-`d1Mo6xQY8QBU8;CULMG(%zw75(i}W}+4!D^OueH+kK+vN8>bdx z(~h+%L3!~Q=B->yLOLHey_7dZ)855opuMSzo2dfNIdt3^dQ}nTylGk!f1}f!(ak-i z6Q-s~D(EzCRH2+i1$VAy8EfJiR`3>{d$1QKt(&_PkRyu=(yP+bUo(tfS| zE%q)?J-|sxX%`9ZT!^TU@0JwgC6;ze96IH-FQtm+7jjgquAK-~Qz8p&lJZJilg(tn z`pJ^&OS5<~75;iotvnm_P?y8*^N%s2t$wWwp6@H>&f49NElyAtlj-h5jRM?e#| z*^^)X?Xp?y&MvsDC(|!ra5OG2BjMpqRqUDSG`WYS)QX7;paMtaahvn_PCl2PxH=(( za&e)Bo98L-7W|>PD|U}G-Yj%+wWhvDOqwp!oL~~-xwf_`wnehaDO3)>PFD(d^1#&? z1IhhusZ9MvcRtw(&tG4~aId%Rr1Nq2y58<$Cn~BoPV5r`Qbr;?ezFMver6;obNo}JEP>Tey3YLV}?xKz>+#MkNcxvRQTJk#G5B7h!AjB%O>u@bDB2Z zj~&k6|JO3vbi4}g8KJJ`a`zXqOpAE((doGOlUJjnr2ZfGuAYg^hl1T#qjLQn-!5+i zmGYi{EpNO{0a4ugTDoR29m6~6xws%Y`>i^8ia&OI(uYSyX+^DNw* zhuW&agd8#DqjI>!V##}#V;!b=wb*DPpPthb@0at@I9hfv2M(V>9%Zc%`iAk5fmKk0 zJ{2OB3|HWb;~5q~Kg-5UbEtLIk|M8$^r_Z6<$X%#&gT~}+LIf|G054BoT}UU`@5tE zE5IJpyv?w1-C!d-rd>XWzL#ytl(-z+>0QN?#!W^jl)xWQBHw?9KES+z3iuMFp&`lr zs9tlo8=10_{yf1YaUD(QW}Mc)YTQ4@l)~#>?!m*u(Le>m;?5F@#OP68&3t*iM0B4Y+M|14=pEQC)lmMagglJP zgh{c*ujU%+%ut#Gbvf{lZu(?9Hxg?XiO#Z|F3Tb0!QsC;VD{3VU2vM}MT=f0;i*Ab zI`yZeIq4FTe|O_>papoW=+TT)VU>0Q#bt|4^@TEN}9 za!F#+A6?Y6Kv^}YX|LwX(CRA`^q4uo{^30{`r6{Nsnv0kEQ~AwS0})OOvf2_yuDU^ z76gyf&PE>dtt;l#PcAx;VXfV{SbeCz#S6CLZpmRX1Hm7md?LuIAXGQb*5w7+k_y@+3xdI(VtHM%8Y5qm(okkD8iz#QeTs)9 zPDxt2K_d_^rZ15bZ_6Nh_nn1I_cZP`V5C+F=gc^eh+*gA06u7T`o-I-=qxGk6OT<{ zc*}=IXqOb)(}F9BHB)BdRAfKtfyqW-IrBJtSfuEhraT0al#pzO&uu{GBrU{S&#G>``qFyn%^t7mwWw^T1+>xONTI#OU!r6n|iK)+j4h*3$2|%r?;jRbo%jF0-sjAjUodda`~U_VIfox6CQ%#%!DXC~@ zkjZG{b_S%CihG?^S}}s8li)sElY&QKLos+8eJ zuhqhowxVi)v@vPIXdC!-#J`EJTHQX3zzhhkFVKF@1SKcUT1GnlODaBai6F!{DEVb> zO5l=N;zl=8uOz&BUgNj?VPvnwk~OUTtbz8@&h(oNbTig9XG=SV*)F{?eojwrJfSD~ z@!$7Jc30DVtov!Idi7Wz_-SLm_4wlf>JKjZIz9LM7KVzcTA72))hc$c&_lUcvuI)S z`PAjq^N5QzqiRn5y$6ndjd;XKOgX`&ZcIDHb}ZH~YvUJr71b%FH~T(3DmQdWNa98I zD`gTMc-fVjh+O+()aF-RTPM>Zv5HK#4EJ_<=0 zj`}C&VD4u3nqJj8J>aF#q46@-YAZstkM%$+FR794!TH8SUYiJ0d*jez%`2!=6l&1p z33%5jGf{2~i;u2GDGnr9XdMSW2DOSt;_OG;mHTaDk&R9D9`ez>RZAtC4#jt}{YhZy zFZ=7m4Tt-`BdNqw^xJuJ%xmu>1zUQhz$Ft4vntRMo324`x((iP=8L0{zYX$5y2t=6 z?Pg2sApFWYhztxT>zwgnCa=TL<(k+{8Bfp9QZIJ8wppu{750a4OAV&)laWGXyis`Is&>EmeQ&%MpRzqhn zxmtH1+8nn=hz)`Brzo~Ml+Z?N#q^*T_d%&XI0Wg;6%tiky}yks&{UhR(dn4r4yh84 z`skLl#n#U(kaYZxGt(IeeaN4=crr=@t{3~zw2Fs?aT@Q!+5*9_uY9W(k#7eWp202C zxx!zP?Gmv7t3SPLr2`3nf=k^GWm26>EOfx2nd>V>?M1b)S9H!?@d?R=8%K314ky&Aoqq&@9f<$AklTUw}7B8+8$*(N*WJSHZglY>jm= zVcwc-N18WL=-YQfxTBg0%#>CI&qfARa{i7UOGFYS4%zm@2 zABC=CHX>FK1F5!N$jwiu;B8(c42IXvjR8YJc zf$`WHevoh1ukpS!G!wu>&RNMYod02@_CM&kI1M>WA8V#oS{TEQgInJ?* zTt5x=gtRydrQ@-p?v583?BZN_)s5#p1!WnaP9Gg9!CQj=WG#9^7ialHQnobfsEHj@ zE{^h#<1BxuF#-<|hphR^AI5o{<`w@O=W&{syJbSwp_SkgC1_LsVWJ1w4--8);xtmd z@=!{l>W7KGQTl%-`VJI!zRHT3Cr58($N%p@U(ME6b8iOVt1+-(7v{meV4-lJ-zz^5 zwK&iR6*IN_HInLxEdYKVCCIY>o#@w#|1;62iP+CAQxW+9k?KX=KPP%!l2q*2I)D@X z!tpW580Uw7j`Z}x@sSuO9~kM}DyIV*ax z-@c@`C%PN>U#DdjGR7wrtbt9$QQ59-&s$hXg_Eg#r604czhN@@7E3tH*5$Hv*suie zt;KR8*DuMsw;*!%fUF=cYso)yU&haf?Q4aaxO3$xdEx3f%PkArR5ucF*a%GI%<(m8 zia0vn8{Y#a3xPk*i$e*{Fdn%w;?j)SNj3PN66XHKFgVl? zkGQI4Mnc^)RqpQ(s6ypcut62Ew+aFiXa@695sGk=!TK)&m5^UL5m4eLk%JM^}82}jZ9M?hu$jZmPTkuok^WBFYVh_ zG1Cy5$N%?*dRFE3d^hJ@;o-U+sWY{q+ED3CM&^Tci&|O6Od6@=?n^^h!dJ83<#Vem z#r#mkcOO^e{?W;SQ%#&j{>IXum40_UZa$O|GS9SyuC#G&p)c)iWct|jOo{c4czS8r zzRfp9jlGh*#rJ<`rZbv#eAdk*?+FghL6zMFpSNDLkR1TuV!$*8KC z_B->!(E%2f#@9an+XC_P^|l08)|J_r`b6~$YG~-T#f{kT` z$%R+{7GzJCkm@P+R7nsEN;!VnZKGCeT(2oIrkv@cd`84MP9>1)F$u_Z$I5k?J0UGn zcMN}v6;FUv3*N)4@=)}Qq+atG$fhi?SQ%(2`Q1`F?8~L4!CF&o*g?pH9K7RGTf!Nv z03v%HX{+JCHn$L$7>(HPuaiWRf&EI%CtKs8 z*sdwjNSvu+8e6EbaRRPmqRpwL_vdB+GiKScY6Tsp^bz^$Q73Ku+h6_NvJ_PVrOMCi zCLKqn;@F0R6?dX}SJWO0Tw?Xl)zm#P4*{B*T`k2O=@y$m!iszbn{P5*wEOIQ- znG#VSYgYi0hd`Yck5YKCJYZ3IQL9%<=7{TdbeP za0Y4}=>n#72k5eHc$G@0n*>hJ;!?jjasdmcuBqJzp>J^wDp2Db*KQyPa9}2*1|wbt zQ)QpcVsehA`y$)3;H_zzNBQ;IoebrNwL<;xWozlMjScF}Y|ggJ#I;yq zL_yD=J*hFqa$-yt`k>SKXFYPra1GI7iS|%gGL`j1Rpz?^6{EPr(&uDDhKT7N|EcMIAzyN01%*p})D*TV7OKNh+< z&Zc*?!m-rGdHBeZ(Zax92Hw_UkfB}xkH+Wd1RiZrg3M@VeS#d5&Ph7y@*z0d7T*Vh zi32Lbris-^>q}Y(#k$x}Ma=DTEmU&pUDs>iNbOw!=Wci*ZjX@QZH*1rDvi~avEpf| zONd1LzzLdm75GS=GE4bmN6OAJKP?{Rn?T~HOZjEE<*wgM&zZT+)nr&wY?s6X*ZXG2lDJRRi6NAD8D$Gd?sJKN16zu7|h>)oxaUp>^C>0j0AKGe#eZgrg8w3kdr zGU<_R%H;K};BVzC=J52Hnol!LN5+;;DG%W*gtvSH2`%Ui97>ita~{ zRk=uPb$ZFef9Ds9Q>N%{mVd4+Ztl|jjrWq!IOt8QM}n|ft+K9js*n?89j4u=)o`)kw@U=%}W$HN;!1~z!XLLf@ z+y`f%e^(MW_kn_(R*-0Hw=W)DSktW%BXrPh{c2%NyR_>VXu@LV$yq&bhQH}W)#rNlSB6Y4)CW}zv`(9z#rm}WnE~i{l|0&SDeX;gK2kTY)et+oT^K#X`&kh~jr9yDMAi1Jq zM?X4r@L7opXnBF?mb~^eg@WBJc}>CoRFTPIGJm@zu?<&K#Ab%o@N-t?8q#IYdW&jk?53K2d zUpFl$VKXa6h`~h3WEAg-UT%p_G)24Z$`O#8)>#^X_mK{qEbEnE>|`CeHsWDsem}sv zYlsgCQjh0>H>ZgrA*{ysdi4rJuiFD3ENffQi>~0vh72=MJ=2s1ch35#@v)Ptq(p6fT>tHXEUyZNAqjhv30n8>h%%XT^DCUmjx z6VP>6`)stCS4nesbW6vQv|iz9Cs|h&vo%Y;1BXpz{d1-{U%p7?L;DRp4r%I}nxz(L zYs%Ll3SA62xTGI|Jquq&4*>BFvFp(V%vno0{6z3mdh6dFo2OLxhEVW99J<&q!1shl$s|yhn3u>%yzOQ4C|D zMC=Z=@Wm!4b}u8Fy35vw8z09!*>gRy%2-#W!!4?If_9(Jig#2nV~?I&$fm;+_t0Ss zCrX3Y50Kk_s(rypjj5mi4>DaeT=pR`(-vOa0NCk}+edm3A4h=2MAW}ws|0~11b6}| zc7p3vSV=&^1-6Y*k8^{+-q^%@u;B*k33>9hhuy?QdiM?G;x@Ixu2AE z0V#zJ7uLAc*HC2?PnvRP=_K5j$P-TtX7<)lSiyincjvl(wy%d|wZTz0&KGzC!+Xx! zZ542+m2v$C(T4l)IW?R#Ix#9yavpj%0Jp|qe)Y(}Y4gGrgjbO)t_lx z(vlI~$M$V}``K=-ixL}=WR>78xbroRgw8F{vze!Bs42K$(0Do&T*lJtdBhu?%4Hi# z+e(4aJ1mBPHjN;|~kdsCskW)#CYN({^H_jRu8v*TOxaq=Dp|?+-{& zH`5d{>61M&NnHLeg{PAw8tF@Ev_IbR6j5c?k5B=Pg?7gm8Dg4a@Qt-*qcltj$Ap

9(>SSP8iX$jO%YBs0D2lg=##YfvwsZ&TEpX#wL0&w z%2ZxW6g6XN$+rewrOjI4=;1MD9V!cD*vlIF^AAUXBa_H|{q7r(51ElT+q7GR?()McLYrt3R0)e&QM#XS$D($ofMm!2TIexHNqhMoMDxtcht=YAR_+NN?xp(%M`0oG7EQ|1KL_-K?v zI=zZ&w}jzk3ge)+>MkS!dOS0%T)>ZivgE5JDU&;#MgeA<0@mTpHqJ28&Z z$s5UtevJ0G-toa()i&47)=Z%Ux+EQy6CN<@Hft-W`GsZ0lj>?F-Q)9wJR$nodpypv zAobrha0%(hL+r!^;_zqrJ-5>G&Yp}SfgkmO+n=bzH+NUoz3Khz*d|p(D?g@4Tj&?* z+&jV(|J5&YFmt6p^Vbvo?Ad-W)35i#k;nVt97l9`$1Nb&?4-xQ)x;9c31(rQ&EOap z!kPYJ;0l-OElXK2w6weEDn)nmzUOt8cRV_-?~xn2zIM1A^PY2%u|2~$7Zo2SbiFdU zQLJM3H^3JRDcZ#JtQq-%8Ez3?7?qI#k(;BV#rcShv@vF0CTRnvqz$BPAZr788z}5; zplk!gt4M)4el3qi>f2H+1d+#^hm0)K15#5MNee6MPq-Tdd01UDQQglqyk=?MQo?@spsZk{LFt< z?fR&vl4;pEEPYRr;(EZ&NOn|4-l(A7o}-C@b>W>wo`LKCum##U1)c7?RDyKB7Cq96 z-pPme4oM*kX(z}|LlYp_jO_O3MeQpCgN9w(=PuC_O?of#dHR(21=@G5mLgG+PfOgM z5jEDQH0us_Q{8CbcD)>=?5#r^M@Nd>l|WGD$YH7%Z(Wt8Y8SYTxQPzsT4YaiYHWDy1uRob1uz%i`%l?hd5Bd&Err5&7ioV6mG3s`A^azx?p{vYK# zm3#9H`D%*%Q~5S17OiZ1B?Dwp`37%W#QwY=tZY!4^Z(~%BX`PbL9RmO`+vP`iv|;H zDDK$006E!P`cwvPw1-F@TQsy)D@RnsV>_25ftJqIJy_KJ({m}1k?|nbChU=*@a18h zdb%zWCH|u*-@BKAXi7w>h9kuybd$h7hKISed=v-mco(ohMa*z4RjmRXV(KJLNdf9L zldq(ME}dw5*U76O+mkMut{oxZ)w6+kHIeC8WIjV&o)USQm5D0OSr>p-L)v2&wTKp( zXov&#&&I&Hlo!|KY#^>_B|ajM!M|%SMug={Vo}crm%~O{B)!Fnz2LHhC{Z+t?OiI=?{)lGel=lOe`zAB zY)%beE+ zGgj-MRrkdyAyp@&j<6XE!tBA#!feI*-?uuicM|^51Cgy^9=MrIc^lCL z$N9d{Neel;%d*Zw8n}y>>7Tsb8t3PDEKAwZN&dJL)Gend{;ZDaTyZAx2?Lx-{9;kq zLkCVV==BBMxJzzke`B%q{(wl)Bhj$BHy23Fq&^k)o9V6*h3Cw(3L&-vrd1E+he*D&y9RL=x6Brcz}y*Y z5&l@fC0NKq3;C}0e1zxW(51|`4MnW+|`}t4pLR@-j6%e~;+*U=%qN&PG z1WV?DgLckiC*ut(j-F_6Hgo=jQcY|l=}h<%M6srlt-Vv9SFN4NP%o)nAGV7wR+JcF z&G)BCYVyqqo_I?W%sL0wEkw5C`itxlP2eHyT4yEUdwG00=z?W_J!X3{;=Z^w7@5XR z+{RX53cDo&2;QJ&0SPhqta-Q(L*_x=;1AkM4IS(iZY>yn?4YjMo%NGu>zqp4kcjof zc;2>P>|haqS_Wre83*G*K7J&Lw~tULsZDBRjX-V7Fl<~*HI*vQi_K~ZEt4KHm@8x+ z`rJdlajV!W=3^h~+EqI0d;CG@;R<8At!*zDeAUjDa^IhVRVOh`LqR z6v{U+*D?@$wiw&WHyGU<`>LvpcvY)nA4>nu2%aJ09l53kcXUIma$%h_?eS6|>K!P#0f9R!d`!m-<&_UdyX*gC9?~!Dp z&rtK+6nQo%@f9oNjtaVZNn#ak;WFkGpeKEJ&5LPL2?pQ|yu;wg^V1!7Q~>%*n&1{? zxj@UqZ28;~cE1>CX3^nOEay05+~uG|p>uxR=|qk(F9sj4i`aM&N#fnQS;i-qvuk}O z;TWV_Rz=eobUNA2(y+9X>TV*9`#G1H`(iW0fBveQmtLunNkjt_ydvj z6?z2}Q>2@^Zk93$ze4(qY7iZ`X5#c1h4^Yjjj8Q}FWv#gcgtvn!cNLkpm>;~;Q7_YnTgpq_kdjxp9F~W)cYLfovPSJ)Ur$*5UUl+iCIx(oG zfKwRPR*vPMgKl71zp~i3W~XB=={d(-A7B-l2RYMwp(k>DmoRtT7#m@EFffYl><7d! zOH2B}PA*l`7Q((G5yJsC5#q1N3C7pWwh}XGWzWT^;G&KWAAo!F(!qO=3vp4?i4eUm zrs*>k$R${C{Nu{#i&R1r$4Ybk#BBigb8F`iYaQQlF;(@4TCxk<8wM%tTdrl@p67PW zEp`nYQHJyWGc2(#6n&)_BPE)HuVr0<}kE2fArz8jbgY} zorT1?Rok*qZcM5r&MCziiRo#b2{ICiq!b&kauw{C$8Xzb{!Y zxAW{!qnT#2ILj=K=$@YrLLS6;lOj!-o<4?JX(T1FUh0aC{Gr&QUCmJ28OqlQ9m%6Q z@9Ot+!%z^>-I^gu!s6>QB+%PNl^;gBJ-?8=9sEs}b+uOGH2htqG4g)35pp@%;X?RrDYHMhi;^t3&nxnJbHkQi zN>AJ*kb;?)WG$YXr;FSs4UEN9D?TkH`AfUm2GaV>i2&T#j2V}Wnv=QVYVMUhfbNt5 z*nR*uTB}mcdhL}Ls};@Go8`?S-V^IZle1CYFh(+TG`OS}`u%dx{E= zRZ{`3_u{-l+8;x=s)pno`)wJe%EYsy<_wTxuXHg*y{C%MmUi6dY7a!#D6*`hL*a$) z3#ahgx*D6?bd$Vpc3=C5W1Lt9YJsJ7J`f*ljmP=vHfR#tHS9Tlgk#0bUu8uwR zqdvM-Q!-wJ@_h{Os>!yS;NSf~A*fM%L%Xy!u345wZfGrI` zX^in19RdIfScPn@jeXb#zg>+^%*vT(_1fwtumfb>%NYzj7?+Hs~=OX9v+JleHp80WC^5d8rei0d}e@m>@A}>vF>?nmhwc0 zS5@M{_B+FJR|5Ym-&;dc!6%Djvl+6=*&}iIp^7_SXvCH9mIyfhsk9R4dvX2Ee&q#U zd4wa|ACqi$k6ZhY^e~EMUIWXRM_O zV>VUrl!2~F4 z)3+qe>vZ}%d~UC#g@T7_H1Fiz#zOI!;iUq^$E_-44d`YUgD&|Cu6M54Skuk22l1!t zw*q8o%Fa;2&Xrnq+0=$YVeThurLOv+<^^0t>s@7IX8W!e8=8grjagHbLjbr8*U-KKN>o1jIfZML3qHZHNVgBC$z zXC~b$bzM5M>er97Le^Ew_Iyp6W`IkhvyvAwYua-Zge4Lr+T6Tb@sfdd-tdd%FvwJ#4Rn97lhgw{DVnoVxl# z;!rkZ{me`Jqdr7kTe0Ank4g8ks$O(ux9dTB4zC|x`KA$@`%kJP*Nj*>hy2UQIV_-q3N@x~+CB`@2dD2*y1-St|p3ahVwrwr(^<$>#$ zI>SrM5d>_2@q91AFs3jAwHHeC&M4AUquX1RP)XKoAI}F*1S6d&Znx0w1CkJXWzcfG zZ{d*Ism_A;L7`EbcQg_4vtV zr)F5NJKt2naXhkm3P~__K=Eq(-Sbor-Hnz+J?AE?-9_$NWvQ<3R~iB*v~95cem#A= z8#<};SX2MX?`#x0C}Gfz0uJb--M>TMxpUKtk5!lKd%xyMMbT_3m!Z1~YrU^qOz2fx zKpQTTN-(sWjzBdG^44^?sph4+nG)6?U06Q2 zdGvXK$_qVQ!3tE`-J^vVC;C<);wEa?YRS{Fm-VokgSrOA+d8$TmU_qlt6Z`%dj{nH zHG_TY(SmihzXpENll-57%Zyv?Wj#};X|FzRY>$NPEFX<_tH)#v2~44<g}ExO_;~{@uqP=tbf!|W=eaXvn49EwcglviKmPj&X9MRj8Dw|&k?tRtMq+&8 zqD^U*7+qZVF!b>)RiqyV_(Rk^x+}>T(i~5Va=+3K&VEq_u#=(8kwvprP~Mp^ zyD@rgA`Ms2=QoB-JOJx?5$DaJa)azo`H5B#zLOequt?IM=)x1Kvr#lJUQrE zc1%g1qhR2Ui|Z+E6H+zieL!i??rx3ZZ!fUSQw4W#So>=#u8Fp=I$C!&#GX(J!w09q zwyYwuqg40(6WjAg;G+>at6VzgrH8``V$uhq z2A*;v=Tr#5&{En2^*{b#UV4sj%_(Pjh=<^(_;$sAt~_lHI^M%p)h7u?f>8c+<^ns| z&z_pA+s}JQ{aZ$$1z1RxlxLBSGf+$;KFbduO41;EtB3e~@HMyeD9CGwyy-&_CHwDa zgoRmEZyKFi?1f1?yj2KK(x5wDQq*Q$&J;x1*pTY$0U6gNJ)b*BLWYKlEI zS-Ot;P+?LC@AP0PHo^3(zvzR>2ACu(AiJRww~TZHy!n_@^*}28K#E`th@XQY>m31c zB!CuvyikfImyuq7M*|Ek!4v5;`}y8)MYLYP7W7SedLm)^F6zn#?e&FX zFRE3H>S)B% z1q#dJ)-X60OGCJ&m@u&osynRcMMNs&xPV2T?A3_Ubu-gjj!(Z4=z08%Je|bO(bUM6 zratrp6zB@rd>y|ne#2TKAZ$zNRu)D)jj8+MXQ=_ndr5G7WXVaX04{eepu5Q>=LM;b zYFmT0+5Y`;8`g%|Nd&RFryPF`6~cHRHy#4_L`wdAyn@T6Jw@bR3YQQ-J%z%b*cV~V zZjBXMWnWn-LxcyF7Tu~U_648S?n9XijDBmwxP|CnL2=*5b&?M|Lzg-|l*FTUGJQ#u z(m7(O;>gg3t|4xzj|5&B)ofU+>W}r$f5N0xSXbU!NO(1s?Nha`o}hK=bl3hHt$Cv{ zP^|RH)8rvK(!2eJH+k6gSsWePbT=AGeb6pmU+5;bFVIxb@}E7HzUvFwLEGM61<5E2 z#<9&5w%4hLm+_*@7l~%f0}-o;JCyyR^CgtJv|&1q?U;=K<3ZfWtf^p$sx-^Tm9Je@ z3i$+C8?jxG3oGcR8x(yurwZIn;S{QAk%dPcwaI_}k{G1(^XhN-NX1G_b>5()`j@b; zsoL3YRJKpEb*12C9gRX`XlGTUrH##|W3S-BXOAhiy-HGGy+?}qBK{wEJay3vr+EI# z$CwayQZU8FKIBLv*$~S${XC5nc#qmF?V**j?3b`BSnFZ`)JmPmNo51@^8)Z(jg83` zt|hK5^P;V<_pC)38Z%Sb?5pa7uT_dB6gk-pJo_9-u(NmD!LYPipdI(Dbxf0PTWpvJ zob;WtZgL|)-m1NSygZ!@-FZj1wEgmeQ5hak=xKe^!K>7Q@xMuK$`lkPRB@LbenJee zC2BrK^5EGZb(Scb4q}>%`*W(z!X8kka${`-u^7J{(WQ49=-7wwQWG0d0{Vw)@cSFw z+sij?>5;g{0^m4Lyj?LR>Qh6k4yn;~RZO|X0@S>MsC4gAUS0Xlcy)ONvk`YOEef{x zYuVati67&KvbEvvh)Qo?@0YSt-u>l|vgO^W;=n9~H*V~#eqpY;dgw?Y9z2c{>(YR# zBAO6|vmGbF=q<}IOO0jski`hJePltZEnTI#IXu&?lYgY5eU)~2y}>s!GYFNTL1Xt&@9e?}p;Dd5jm zqu7o*5ba#nAQ|H`*m)m~qW~RjgKVn@ts+H;p=v`K#jh#Ng;{*;-UZ z-24BMy1sj#uA4oUkV<#M^=^m_iW$0#?cMOZZn3J{fi>*vZ^BX+j@)J5WKGZ!=ncXR zD!9!Tgzlms7=wIlkk_5VU%F$1nF-AH(xRXy#(lT8E2v?V#e@20s^b|C>tlPBV(@~J zu89R#^MDeZ3b3JNT#E<#7C=c9hq|*nAWGX*){j>}*MY=iWy5Gr)^uR&udZV==2#u6 z689*oY{Rnj?c*%vU2FAb7I~u)1?`4P+GQ$q`*UK4^CL%sPy0n9X8m zTOKjOOU9Y@UB;OfEnWc7u8S@yWy>-|vOJ5Xh89Y9wgu1aNXLca8bPybJ3oiCs7aD@ z@eoUCu6SJZnQf<4&SOUFHsdzqj62_Cv^J+<_wdOKzN`qima)ivkYXbHEJA>##=I`CuhmWliNKF5Hc^(`fs`bQXj^z}(X;4Uj&T*tPmT zv=y}&khqB#i-~M8KA3y2g}!r4k~kc5RKLnmfQr2%Uo3wNhe15WraJ+-YDpRo`$0vL z!whfp)P`&*0OyyuEn4icOX>%=BME9|HTfEpf}JX=ED3@aB?fj`Z_O*hs_MaGE0)LVHMJTdK6(0;#{^J@hVKjE9A1irsYD(yI+p*FNh%76su zF37V)LhIONn_P!m_2M0ScPv%6Cg^p0+02aUw@Lx4d#7lbuH%JL<}%tFK9o$t*PkqF z`Y;5O76dH&ICffGMi2m)ynH(IYDk@0O&wd!F@3Ud z1xFb*k|zu4HwLKRJ)$sH>`#TjmMGLjw?fj~-^?H>khrU(j~>vuDF}A0%DB4Si8$!- z=frn02Sr^;+PCrMR!}HD@=$eO%0iV)ii$0`(5{8!RDtHGp+|H71*8BsGNaH_KI?^c zGaM~P3iZ-KltTM9q>^7csj`^4_XMVas#d}x3gV}2O% zzGq57cuWkBQ*opTvsRbr`tt*@H#~f&3BwWOcx02XRwc2-T*M>(MK$d?xCjnuWS>MI zTey~KV8^pjN9p~6fl>cB7GjLSttr@Cf|VD%jp+u65qO6Rp8IIIJ&H!sQUUa$=v3?} z0W_JPl~=HQb806%*8Q(v-6sLT6yVXaUk8Xtulz?A(@WSk6PSAd%s|TBB!Sw;1KWb7-pqjhL1~~J)fm=|u2xDK;1E-XS->mA zhm12Ze9|6iv7qtpzfmCR2Eh^phn576^aE|_floSxHYp}}{9N<$sDAF@3lcwJ)}K(h z5Th0b7g3@f98mjv;Ic3JXd;7j#Luzdg@I7CWb@o2Ud5J!QE7`BfBFxhj_J$*w_u_K z;9n9LYLQBFAR>g8z{r~;lHyoFjSM!moC9R+_+53j$#yl;aO zwr#r>mS<(+@BHWjj%`f-7q)G$b8urZ0uz&lD&qMtce8|N9yLG-yEreEz!!NX+bR}J znIEUd-7ZSIAVI#D3HF$v9EZ_HJyIH>e?>!nq4F~{OSx&01jnSftIU8z(W(n=ZJ_vD zMJ$e3m>5G7FXu;NnSXaJyz+=1XchEF7xk}Ez_YmM!kLSlKZP^b0}NP1MhtJ)6*j|Q z8ypKwt_H6UJaCXw!to>c@*h9IT^^Qy*q{)@l8)4_$J&+t%!S>QOQBfOO3oGRaz{!!w=IwCbSnpEmPe!wF4j5xeF(?!!9}p+ z-v{xdI~W)lhR#9Up`DQ_#F8f#@=V}0sb#&iZ9!s@9%bZi7EDhfKyppKHH&qm^5SEr z#9rDZVpKvzR{Yc?T!I32S7cWtda;gTn^v(FOkYXB)r%*BLb|RsqFl1)V8mBQ{AwSg zUWnsG(`imzjx(P;w@1W?uhZOLp;T&Z+b`%HWWd>^+g{HCx9xsGt3h;w8;u9vLm(1ET_vv8a-ZaYfa2R`z zyB>BE0>PKeaK8ZUF9=X4hfq7#aNYlL#X7$PI5-28Eoa^)50KuiweMMr}_;Ye68c93sp;7Qx#FEk39K)$|KZ=?=O4g z!$$gYH~zpP#(pm=?uWe`T#`x7e#ld3W&hydht8;SXO!N(Dm@zM)Xd^U%&J-akcL*Hr^*5DHR>wBS z;%}yGEsLGYSUQGxtt+FlLs9XN;(ol%s%K9-+7?MADeJ`(bqoT|JqjQ&n48H$m)p7c zgJC7IbT zR}*)@MGa*=-^`|x`#k)6?^F_lbu%g{6Ve9s6NG<(sBAs#3BNV(IW*KwjWZkNo+- z-pD#Taf97Lr2_4N`vU22L^ftBf-uIdT(d?D(28zQG#S{)2iJgsMeA0kNQIVE2Jr|x z1(q~+3UdE_@z=aG*c({6CgOu0hey7<6-W>c*H!@)c(IbFo8fR>1xNxu`Mm+^%_Zi* z(Q4>e$>;Qlf1JTZ|JgqFg~RULn}@IBeWIVk9Zqrp8Y0D*{m-na3zX|w9rU981#kw( zLMwi67WKzL4&BY>H1^3Qb^0`ZYs&L!{8KAyxiok`@XVT9fqB+5o0z1{3%sx-hXj@_ z$ql@iavubir&t0ntu#X*eWe(9dCk38dZDEQ(j^F{l&6q@5{sz<6`A*ZZ;zLFRh!*+ zlF-Lh(#;~^Q8l`l+$(?NFpxM71uu8voXuKakGnn<1U%>HYDOK)joUNcZVEPlPVA!U*NF%6AR}eZ>#UUV>UljjH|~2cGlkT$~S%8eqa7 z(OwVCSrvPv0FtdcB8Jd@!oZconF?htf{1?aA}VX&N|oqFa}2aovEDliL0gD>6~0QA zK?IEMUVN^qQmDA#zE;jcT0^hP=R(xa z+9+&&MgQkDFgk{)HkQA&5Z7(9rhlIWsBqr53e+d1;&;8@1Cuk_#R!F%VZd++>_acG zbX4JGAx*idnmp@4ltA}W6ev$3PMiRysxqwo*RYanKr@&M1&PkJcsTZ|jd@j14p?&r zpiOH0snY<}tgv&5x=tE3micb!J?hhX+(-TyUj#@Rzm9T1Up}+1TI+D44_<>UBK-v zv{^W}5{k!cQF|sd#5=y|&KPD}yP+Rf0O_eLW<(%10Nv~;F5*;ZpPy%QY{a4Bap}u{ zg_%ev;;l$u1OIadlT4ugIfJ%a1!d}oieO|sZvWz6sp)#lUk-yOWV{kEw+q_N?U5#V^qds$1$nWF|_Q_DvoK(2(@%d zEqkOEr*wuf5vipPvlBT+N%K7cox}C*z5o0AuK)FQ)v!obq8zf;`#jJ6yKf=ncxd@* z6QtK=)`C^31(w+t5UEr?bweyG43F}{;7B@ zcp4rtMTFL5yH;0(Rk5c1*2v3zaeWvNv5M^+Peuw8);xyo9-iqh#1a%ZP;6ps zqi0l2$7Ltrk8Kt#JIvzVU%l2RfH(5{Bh&S6j3DWv$cv9niTlN;aIp+Myu1dw8HVU2 zRID1p)AN;(r*}*6WVpXE?o1OeT@ocPPPh-d_o36GQo=d*%B3}HI$xryvaI9q-T24u zB%IM2%Ge~%RT%l&7eyvVdu8<+Y+uCruO#URO3_i)y1tYQqTs8P43&~^OScq}XS8Hr zf^$gxR-*ITB>3-!`}-tV(~fJX?fDeBeg$2Lgt!1HAv(*NAvhRx-Lr{>A;MyI@1xSx}LMFX=goXzp@95*5OeT6qHHqt8tk6pd8j( zl(~cJEV)-7#_{RUMoaFun{ema6BeAMVlVz`OnfOMIKM>Y*?uhhE_@3+`8H3108gIT z9KvgnLyUzNc{MbyeU%5!Zx@4qC@LJ(?ycY&oCK(G@9q`(!-Jb3)*svo{=U`tLf;-< z;T+rvXbyfrkHcBK?0Dv6g~!m&+=Z_lA+~Q2dkw%#16pNQ@g}FR^TMB>Y63S*{nm6} zuEYwk+tl6RWVRyP+)yOo-3R;2*EfZgDm|2(DX0*vOM}Of(>rQR*i`cM5{pI}cPXC z2MG1uOMCZ(^Bw@2S=SbKh41V$tV%c+O1(b|uVvpqapqPZKg9 zFefxW0DBYOA?yQLzgr@FAmzux2R1GI=cHFKKz-j1 zLBDi}*C+51orf-B4eUy|TYMg^==DHyiaC`|kP!9==c6R^UTYkGof)06>nY$MOaC=0eV zOSu(5pC34J(Z^fJ#qRwskviOw=Ud3D55o+u6P6QRsNzoG*ImV$S@%+l+4yhm*bwMV z4*yv0&Jv!h2Ax4!%A#re*@Q&l_jc^u=q0(V?!tG<=FoaLYtH!*DYIdm+SM18S$7b^ znH9SOtP0saQcIE_3GZ~SU_f845*D9-qLkRE@4H}L`0I9I8EK%t%>c4qK_`cEPF@+& z@jLfkReo}CR|kRj@vE+Y)D|>dtG$sAuO8e4ZOmsU$cQuvtrf@VR-Tp`$_V}cEuGGo zX+~~TiAxfdZ*1VE>g3c>NR=nQW@#jkyGV6dwuF6rLjt!PZKE_u*^_eY1u)k;x?pFS zw2?F?X17b_Jgw-)Zvp0bulWKPBmOb}9-QR@`TROT+d~m|!;vNtYQauWy=`NgSd8sr zy{%GeGlY$+eget7&;L+-_#3?3sK0lMxSMW0v1E{`)A{Y#@y$1SYcEC=!GEYeW)Fsb zi(t{~SmZ>s{2rK=aj9h!1Aq0AUBd()YuTmz;^3y~7cYX*y`oCllb}-2$kcv6M|eIJ zok(C z%L|`zzHXaQxdOJ8^Gh($;k>hWM+(~PZWnSlV6dBT!j(5y)lfSq^uI~f3-S}e{&*q` zHIq~vJBm|*+I^Rn;y1_Ik#E)af~gced^7l}Ktw0nGUhX=mMLPt{gdE*;kTC$-$?T_ zM?r}xw2=1t;SL}-bX8QvE|?PL_rt~n@Trs(*uHbYpqiLqKC?TmkUB ziGigv=Xn1r+CyhT=s(dOrug@S^-0~mfUbnq8E*Z+gT8VzBFGVUfO7gz8FEt>C5U8jZTlT`RJpJU?Le*|E~MbS7_z;+%J zZt>M#yblNV_=7or@IR#Y?FRfw)`6&H(*^4AH@hy>9sEXq#Uj5E+A1dH7*3e~Yok10 zD9^VN`E$$flT-KVbOKerP_?6@R7IuX*j)hLz1IZr>Nn~&gYw8<^5@5=tx3C)cEf1r zZbl4>@bm)L?Lg_a?N0kRbbqw&T$1y-~`8+JlR90bdn)0 zuSMg}q9_x3T2o^q;ucB#zJ9pePXR2Iqi+kR4^LXQojna^OCoIlcVNx6f|4s{DvQ;y zyN}@ zAr*M~ZEuXkNU2n3Ja0Y!tsMdJUrnIfHJ=#r9?#9yJwjMRl3~tYwc8GLJ-~3^3haA>V1~o!HAwxre$&2Y? z8m9MtpjUpR&qS!oU&;<*-n8(FfOdYZ$BQiH*BdfJF-m2|uzb#6mibAU@`W;Gdr9{b zW0G0<0270tF#`GSx`#Aqe<6%#)08Dl`&_ZKZK)~bH{c; z9XV*MW&$srteQ8xqLQ-4yd|Pfg!#T(8V>=aDtlpJ;&B7kkW4IqYB#feMZXr?<9+^=M*~0B~ z)Ru*6B)#^p3M}(H=wD|&m@4-R5+;HN0x^)!2(GT=G|KTq%Ga7Ml5LSd05ndb__B*1Tm-GpVG&>X=RfgQ~5*Z2| zIo{B=O%NZsWP{LyXX{On5W8eb4`_p6c!?L)N&i{O!$$hNJ!yB_`u|81<+7Cd)cj?u z@W}bfWx{)jDAQdIebGa*6VYR4_VaI2&om_JY>^HZ{aIgF#$CnpY#>>0vi{m|+~5su z=`R~B=L~Lx*N@wMeoOnmhdv2ypBpSqj~eU-Z;!9XXJEyKXU}ZhWnkkBnKeGco|<06 z@taQ?EYDb313aGATd4dd&3aE-r#Q{zztt*eltl3cFbidB!?>za z{JEw4;*WN})QH0Fm0HVjl~$?sZp*do(kXcl>0nn}g}j5}T%s(6sTn8gzWgEP`-+ot z2}EPN%k`wpA3X5=$x`(Q{QM#H)w7)I4Rl0e=t5V#emqesnw#+*d*@+3X63{iH$s8^h;<6QN zqOyp7gOKDUHOoVkK7b=C#}t}uB?*yMOk!|X7wGA@6-hqk{;qM zuHEChpgq1=>ve5BJlvi8Y;DM1Yf8CD$IjJpHIiL5lEXDct*V;2H4 zrgkd_!nsr^(>8^LMEg&hpPK?e|*M3av!hN4HtFB%is4vvxQglw`s{u;f0T z1U{8;%FDr$&lw4PEa5op{EKn-cM{Gx7%X`bchT?Qa2MY)6qJ{|UJs{#=%e!Ln4=Hp z=VI3l#do|wKXKKtafiTiF@5Ro(X~3Ke+#T|-rA~n23~@mV;%IyOTnEG)r+5nhO;=G z#o_Gu!`My>4P+4~pjtvhy_+<8{)B{@w*tiS-8?=5<$@~kAcg*sg7VP;qf$Y&DIzz5 zpE0~%#U5vfNQwSGoP=7Q?OyTln5 z+@7Fzqe%~FBwpxPU~{j-!cN2lk6=A^lD6@k5Nv``fa78QO6KNv&OdgM;@P(D)%o|| z3kH%}mIds-gVD_M=yQsVm8+lzx=vDuIwDmEm63O;D#mV;e2+tQ*;m!lP+;xTQ$cbd zkmyM8EYrF2`1@vTB;#3`LBRIncXL*wQ!#;M8&Q&$n zy@jB0PXd^q>Vq~E3)%#YQbe|{?Bp>xgLvo1u;0z=Ae!-=BAXkdqXZb*WW#ic9J*5{@O{9HBTZ=@&9lq#W8 z?vjrG^aOP5Ku5OC6fw`&krK~ZFp1Y2U(^AsJSHuROS&XyHF6O1)J0+AVT)XRj3n@f z^|FhX!hW3B5x;vAW}a=a&9$KO|0W!#!~%D-FF_>zRsvd?{VB{V-%mv^xrKI-hgWDZ zA6G;Pm z0i>Vk#jeBsIW8jydX2J?X>=(b$kP=@Mk|YgMiDb+3_0qIRXCa8xY%ZyHTtD<+ z90szB8t_Wdd6gm7xy>=iBk7!KrWRYpi`Ovd<6@^O0l5IL1xgqMI;QJQmIFOnD2*F|#-jABihVV}E(~;>U_ZEU_ihPCcjTr8IQLv-&;0=l7KwP{ z)p?uQNw`oa084H}8hX1$QhrO1Z5>+k%L%7qiq^G(vO}4G(OMn3(*@X95^~15ZHbLJ zl(F%hV|r+eZkkYdp8UETmxNVp<7uk#9pU=T+oroBH#YJ;bLaQ}tf2AUTs9>a26Lfx zd#*j#JA{Keu7N-Q(ap-~M5&{UBy*UTdu1-THkRgC`hO5N&BcWoa7=)7V(_YWFND^p z=)l>7CP2j=YN(u)7MoJ95P=!-01H_z2c^->LvG8@?`QKUzzg9|&bQz(Gxblc@uh#B$? z5|4FGK3EQlE!YAj>`&%mhrl#fj;)jz*||I5vAG4GJr2uJ3)zjR$@0g-vs6N*ac&tH z94g5i!ag+@J5O)u8Y7jaxt&?OqwSPKe-EeSsih(qG$%fZ2krl|=Ak zzWp&-Rj8nnldD~lD|AA`yl?etxYkavnArapA|nz-H9+yXdv=>ylL+-k^EDJ2XLK?l^@*t( z73=pv!s=-Rpgl{iF8pEahRrJDT%B{S94O-mc&zxh3B-V|Nta^@`iKirH7&q$z`c~XKHe4=37u$vVPmmXqAI?}1UP*2 z4Du+g-9agPz#)P@$yclESknOUT;plQNvSwV>kS_)l-TdZQH&`IsBq<{acLL~lvFLT ztg!IocgZ0Y`!X4ESY@=-ODEI7k&oU|LTotVG|~39H);9Mb>P|ZuoV8d4=hS-{ixaL zLb9=#N6bSiu(!tXi!E!K(PY?z2Wra`n}ucgeMyy~LTtHv5Z9cEaK4fqx&|(BUAP&Y zAj9s@0_#>VW%lvIDilukiKnR8B@N~ILcDMw*I&d&LV7!P0mI+JaNvwYw9a92p&1s- z!lVi=sUF^|fMaVZ$Z<Ts345v>npZ&F>gFmmbLJTK0$&7p zbxV^&sdVVcUYH2CtT9s2Bod#mj$ca`=Y%`JTs5a8BR<-!*i>2{c1YL>+EAF5z{o&n zY&r?wK$Gaz1TXPcK(%*%KNezOo8U3Yd)`1u-8=6V`AA_}k&mvMaBa^h z@)Y^5w1S4!6_K>Z|5W5F@`>aU`5;o1sO5L%_lD=P&MD`VH+18i8u5DYT9-7HH^O?R zJ=38(LU((ngU%@*J~sPja5B&H$n5UyWey(??S!5i`t#0+@@(kov)8xT`|yhCneH3+ zOsjlPs)%R$vS+$$BF)z|p~L%o$Gawio@sxAD^V)Z`a;`@T|x1QqbJ5xlP9tVwYqn9 zY7MRXwJNPIwROz(=wYn~4`NGGwVk1Ze_&+5|M?pCT8h*(2#)v0xZ-nTqUYRgS<@TS z9`DuBrBQnNuT>dEQ5w21#ZyC99a#NLHJt}>)hW16Ib+a+I;!R(8hUK{aw3?7S1w;d zx&oTablyXuC5w4yYzCcD+!}8#aGMKKn#iEJ;N60mYuNT&jxw)p$?Z^?X)UXWiruVA zoU(4vWzn82IxnlBg=EpH&?g1%EV>1<=-6(mIS#YV4sVD<$6*mT>Em^od+Xww7PjeP z3l_vyJcYeh+2scaF=L$JHYy=Ujm7f$x;2KZ9Pu5SZ18B%0bTTu7>8}JJp@jHsb$LnAsmpJ05 zhVN_Z!rANDH}HZp(glt_Qh^@Mt~gZ%EFIC~g=NYte!m&oD!ov1v=TBN?$Rr4Eael` zW+o_<*B zO2sWn!BVhh7hgrTaM{Zb-3rFEKB?zABGu3n%Q;0mdc(*=YCOSz`k%oAZZ+(*lm>*m zITxc7@8T7LFSc_ucwmT6Lw$Ne<9+J5=JAMAxg{#dRc@scmMHeTsxA?$Q`KVuS|syt z(u@T(V}BX3k9fyMGW}m=j2JazUyO8U#x!FiUd`C8(>}Xq%;(LD&>Yo_z5O$v(dX@6 z?8~fq(}xHCVUN#SGW!nEjMZ%Ob@=cpPlb-G7_t9$#QV}nruN~`E3U23jh4*5TQp<$ z6n)%2jt4H*Jgym2n;a(JpYp~v*-0joW-P|kdFl%jG5HKP3^z1ml`@;`hh$QEqVNWD z!*_z0!Tvdy4Xpvv_GtDfSPyDvO5r*!Pwy$M>y`UWjHQ;}BZo7Zbel^)+9XShd1i!n zYOxNfxB1KVfj~LJj(Bx&J@obd`P&inV;d{eqp~rn_tT-{kO`Y>) z`n>n}xK?k!|Fln;>0^EP+}}L!v-`X)_$_Gm75lvBUp(ZyF#9d=dGlW8_P*jf?(?>K zYs#rJvXg>UIU~3V#){^-&|g`F-;d2B#lINwE;o*J+&O#rwMpFG!q8vu^t!e8C}6=5 z*{Fk|l0tYrmviYL#}|Ks>bpi4$_2KoCwV*@#u9ZUAjzFojCH%B0v4iV34K4Lm|(c2 z1+=hX`^)>>rI;7EdE1#(EhN2MCE61wqAAY*Mj!C)0^y+L<9SM+M7xT1Rx5*<%=r!DkRK;59H6dR)!PQ<; zNlNl)@bR~8i;c91xozOa)kdZCW(Ad0JRqr_n}q5;C5|sG6==PWNLNH}zRC*33ff@7 zMX^J2s+INk!#5(3=j3&0#3O{r30Xgyv^*A93#&j1RqSg{(4GTnm_GpZAS)`{Z7^|-{ny4pr{iDHeYvc6)9S3~lR%-{hFnp|I94a~gk{aA8B zD_B)w7Z2vmLjgj4E#;?4t2nkL+0#eH=@@kwxRS>V;xR=^eEO}XIj>Jv0juFW&tUMD zat^mw8(eV_l$(vqA(*rrYH$!$!(#o;YEHtSkZ)Pz7Cs};zWD|jnExh+Q<**J9nUs{ zDt7f~1JSa#rh>6C(??n<6FvHHSLBz#O-H+!*rD2xo3893u&fET3J+5ZyT$T$om5#WP13<}xsqpz z!*Mp1B#m?rlYJn}aq|*!U0z1cgUf|LPr64z=psX&PT>%-vEL8CM_(L*Yr}A} zA8rn*TEbhVF5@NJhgp*SI0&82qNcl~y{b#%maqX`>p<_2{?z70o)cAMsBI{TM7pw& zZ!lx(i!RkQk|7GKC$H4e16f~522{oY>Ql=CoA{k^(XbTlf_hY=_YM*F$o#l0lHgI@ zELFpFBbmBf+>=~!p-Y~f%X@k_%LKzZ$@494?B$dz50_`!mF0VQ<(U0Z?iH=MwXK|e zQj}btSqg5dhh`Kx|9F8wA(k;1eZre5OwlRGJlGV^#3)Wt0dqZmi`x;ZGqi^4;C5}GFc*Tugud)&>4=#38 zkJAR{JiRJMHBoirdd5=Kgy%!m1hW)fA@9firMiLSH}v5cY#`aAdSrpn=4fM7(3U_N zRYx8CMxE|no7v>O9*F#Cl`ZQ^WwwXZFc7%fTYea2;k)c=lOb;meufwKbz(WMgoLMhCX{JmHPYlGRj>4^TGvMTl$(pdvTk|nQl^eLFIpq@DQmWYGh59xM~%`k zvFTNjlP?TN`%jiQ;C;xkw#m-jy?ZH-EzW6R-45YR9d*pz#+%sGr-;JU<6iwd%b&Py zk_fZ{dKpWQUmC(H6&;?pIM*c{AR-m$c?$vHEp-LfE!>mhy3q=6g?B*(eB{oo$gGso z=Ty7HO&~j8&Tco6t;H9taYbxZb%9Bpeax9SnmF3KN4g}D&Um0VQ^KC>0~4Cugl%mu z(a}Q=nCyeeheE3~7{eN|h^;G)zO>1~9o!CdLll~i5O-6gg<6W3-J<+FVtHx9M_5FJ-2P@c}l~9|qItxx>H5i81 z2)l^1N}UwM1*)T}_Jq=gep>~`N0Y0bZIi}g``pd1WNF!X1-&{6q_JCKFT5FNV5>MA z=u@_%JRF=o(YxIrR%BMOwZ*!ndf!_w*MNKOp(XBSNTStdvb2;OIUH9lSgqer#jdWC zR>{zZ|7M+au#TYMN9OO0d^^7`t25G5Cw;$tjTSw3bgZY&5m%@B&C7|>c+gy_)2XPB z>rAH*tJvQ~xpj5A>c|YXNm2(_9B`5uK)%IZMQ!AXvVL*D0``SLPNher`q**2ya<#r zMVTH3nn^!g>7An{fzlJDzHic`g3``H1K4GT%tj$L96VSIL$*VX;d|@}G|i0_{J08= z!MUHJc9Xy!2it%`YDabj9D#;#tbrHV;BYQ!X+X99xCbUiz#gk3SJz?l9hJA4v=k0& zNHcrI21wJB{F+c}xv}`llgR#;LvEHlR~46)k&C`+n!yaRAD8kfc0{&cOJuw>K^YHC z62@?I+j&duZ*kx+ebJrw*-I@^RK@UuRm_|CH zHMYwmim))L<9R1=Y4c_|#9{J}o7sW_cQAX9^2*nc43zS6 z7;wit2~K^nM-NYBkz+VSu?b3x>1d@>`FT?m4>aZMR((Dg0bNZY|20WaLN-Z zGdg5h&^8f_aeqjaQA7c0=)%v~ByzTyp}QBCQyG#|o?IR69-#z%B6mS8G~yU62{TzF z7(PoK-hfjaSAe|4Io(*4BMGTs^HEju$iR;^5l9@ z)^n*3)x*`r1DX}NkczVM!vm$=#kuZ6T*bNT;dcp}RcG!&lB-l@>~CDyuTd3p%0v2u z(n45V3Z04+1Ecbh)~D(~Z(y)D=Mv|#6}ga>3ukk=l+s+-lnW`jURVCh^3ZZO%%(>0 z@JvY-9MQvLdN`KcUZ1>Bs!5jWRcoY-sv~8$8W7xBc_SF32Md%FX3OeM>aJK$>PpRk zce;7M@8-=bi3L}%d&Mi=yibd}d3S+-X(|FqqFo(#?cUYHd;CRZ+Eg>@zaoua8)K*R z8BsHnNM7XZB(j^b%nl+cG=IZEWULhYTa-K;T;d243EP(G49doSw-jOnZ@&OmsKplE zre_tfQUMy?`5JaGJi~T2K-ss}!Z}{5&Aw41*gjW%R@e(?tD=_SC@Ymg2WP4$d5_K? z^*rKtkD7q)40EiV&$As>f#VtQ3*UZ$>s(+&mRZ;-Ob3h)w+G4LHTD?dvaf`iZOhu> zz2V>iiK^~rT#rz;dnnS%c0B$x$qu3zxf3ll*5d2LV4slzlBQJBu{kGs!!TYf8Hf9Z zM3b@Q@A>Fk!szE~N^b~h=4lqzZq;DE7Jj+d_9(pJ)tx0Rm?HcHC3 zqJh3}W3|BZIQrWq4KN?uG;#GC|CR==rA3)JuwNNq{2q8{(|n@2dlPAk?gopyi2Om# zSszn#$L}L%6Q#~9lS){}vnCCjv}7!eQ<;)U3Dd}&wPX9VE|wO8h$JrgtCWFD)Z8WC zOV|h9Dwa`KYdt;li^F0V#xr$WiM0Ile$Mq3D$Um zw>F%^>+A+PZTBA)aJpPcG#htuL}L>2%L+`1XR|um&`~xl5wl6@7IAlC2AiFM=gTBD zBYaB)^ocoXutZc$YxcBav!YkL=B5f@)e6g+OBAHLc3O_Z*W95Lpe*c~|2(ww(v^A3 z9K1>yKepfdb-(wI{hYJk`>$KD-@8KF587p2DQuy~`?bew_i!F>Xd9pXFngO{ljnh& z0ogmq!ac+QESum9b1+k{&-+9l*XO;n1@7!v`&j?)7x#gBDd{yCGl1#sd^ZdI8NTh4 zsy7j1p9%VOd3|I@pEaelPp4%I`Z_5s zuixEJ3#aK!*|Og@(-B1$PZ4k>!(k0Ypp#9u6Gxb#%F1g_DXJza?E|kCl6O_IWOEpv znRg@C!fA~^BrU6AE$Wht!wQrd#^ zZEROzw9uvc=&$Pe8)6P06kke&oa}~Is&0wK+#i(^f5Tl36IHhiK7;S3);qhCb*p9` zNou(3-v=F;9nUx9EGwiNUODuatxysrTJefWwBnw-4n;iI@YAJh8YV6RMqS)nxxmJk z*VOSFq8la}CVmnaW)A)34-FG-H`;D!y`j6>ZWtzlhaQj^8}5}`qigOhjCU-W^s$bJ zF80u)pIm9mu{`KR+JO9-@A@4y-P=r>?j^2&cl{7?&GqAhJGVDQJML<_D>}LTXTMZN zlai(c95u0;rf8<=UXz+k(CG>3VORkDEFoX5bE0CgUMi9*&-9%GSRX6r^g!k?Qw_G zvfDpsrOf{calK_Qwz@(fmG?y#C8BfdmN~EM3Dh9+3cJM{u<|#N-G%b@hc0|J)3#Em= z(5CeU3w5%>&O)3X%k=Pkxdhy$ z=dwfM9Q=wWD<*$ZJ>8unpQxA&)lgb*#pDm6UG=16iQ(a^ee~c))N>@)Q2GpXS74&; zH{q0djxV2!;%xpGMM4z%q$W5BOc9FbzR7qA%4DiU*z*y|`-DLU1 zz=~*tl}v?xf5x@m*`2P*(Hms>Veu6Q+J_BfzraKt9awR8;FI}z17mNdra9;z4~#hm zR#`GCSabe`mwz#;)K9ldGy>0?Jl&=0Q*`@u6umBr4)9x7GLTUT<#Mn`+oh?(!O~U z3;JG2n$s5}FYJ3ErSG>^3^3D7_6ZJI+3^~%_suKmd%{}UC%BfXjdg9ILJkYl?IkWOB`T~=|KLl2) z3Y32cTnkKo{*%C@v79JB5BxeXIlvBSX#cvvJk4n6x$9adE@zGQ`g-xXvmeKCxuq$X z&{L-dg*LHq`~mBersW&jCnt7RD4fmwUP6xcse9{sWC zjGYs`_MYgqr3-2qmWp6@7l*`L8 zXGb@teD(;FvDjCgdJmS>?s(&m!2Iv1hkz>046|(Zq zsB&q!FSOw=YPl||ytCY=@oO z6~UfQj^X-0n(0~5*|TDQvU5%#nAY=nPR|NY&x*;O75-8OX_xlMI%QFtmrczocoL~{>{5ouMlXvC_wX(U4`9PJc1y>RDi|O#V>PA6q$Psm zRh#Tp-Dk^}oBFa0CS@43+3imBC89{S(>CrEWfOYeok@ZRyT z(^*D;!q$u&viW&g)*?uEe)%VF z%{4xYjdj)7&o1_W%Dhw(?w2a$Wx4*GU`z)Nx9sL{3zI0fQ7w8hS`Ut>zT`v6)S+Z0 zb!f?}KZEo|wkB!f{-z7-S#$N79?FQ)vhgKRVX8$`+p^Q*I}W*6nZE_N$NT?eEFBAOy$q0iwoe2)cA0^Ft(qM+a6V$p0t07Z8PHqKLv(HMXHY(=4gQpjqi0O#b-E=aA zJH1MM`YEC)#CYOtd3#zD$a+faYoY9+9{o70@G~CNMai4VpWwK4&8bTA0x02lXswGX zsKqvo;cP3I0z_Fd%p{F7n-RJKb6UQX4KN)&P!pHrXHHufNIC@~WkDkQwo$>)<$u-# z6eb-R^3CFnvO2L8e)PBkk`nnT15A>SQJ3QJ?tZ2mn~yB7Y<4nDyvo0U&L^sF_qj;~P&eX9BBNqBM;u&UP!I~AuZ8CCJH39Jv_*tq(VEJzpW-Dpy{IXJsa4u5y`A>X9DxkQ1$pcGzISJgsqQ-K9D6aV;O)ue5{;gXaF? zJN3aW?M$g~2yeCT_p=yzpCgrqBy>+p4QE0hLz~tcy6y7@rM{Y%vS0$WGNNAyg*mwr zZ&j?Lc+&~2NBl=d2f}wCO@cFApR`U`^6XRdv_DeGzw_|=*go%H`@HJOKINUl!LOYu zCYtbeNSvd9v#yx-n6nhsKJUr)c}LnOM^V~8*uIk1waRE}%j@mGZl7FXs)eKND^s?GWM1s9XrfLP#N$X< zofY3rC%v(p@i`s9?PCnWg9|NRYNTq#*$i^r)y->=%|{{d8U97|15Vb-n(13w>deVQ zlt>DfA{0x+ZH@G}BJ3tD%1wR!>~@j!Y$J6d(h%j&Lp-W7{4>hQR%CyX_X(JHFzhK% znmgoFUPtGgT3+7c@1emAW)Wmzt%smccoxUYg-WM^S>-MVA<)kS7g0 z+Det=T^$!A=qkM*E$p})`@0TR`VMX ze(o zmfaA>-9#lM)gg|p;3b%KQZ!<$WwX@~TL;Gwb;S~DSDc>OMr1fIAkzIx)HR_?VPcdi zDzlN1z$+3K6%C#+35+q+j^?JqVh@Xlq}4S?G?L--TeG=8?Dhh5;4Hi z5;@+c+i``KwMqhZg38ek18T|E9m!1 zcYcCEr#HSIgl&E2_38OBrRD(hN9RsAHsU zQl%jbTuHT$(E5e8;((lS=ujWm*bH20MuglQYzsjozcrx&^0t`Bs5Fc#?UWHd>Q~T@ zJ?0S6O8?UKC(bpD^Ds0N#PKH1T+!0bPsg-OHt^0|RHA%nTyTPC?i7@)@7g1p-*kLY z6T%s4X;>@f>(;?oxwu5ts82 z2`|pUsp!eUaGP)=9d*6ClCKLYH!jz(8SJzMYU5N-jc#17L@+A}^GD5{22inDHSaj^ zRGFe8NG#TdWkc`Z6kw;ZIUo0#5Da#jxb&>$ij;RP4fa3u9(C6;YyfxgVHFJ{;>lWdoDqt6Z^#keJN13%G#Rg>zgXs zt(-qnaSvk)$lbLPRk3@K?TsWWYbB$! zGB$m_9HIoZb7NN(W*9Q7r@GmVFGG~s8t)O(od14dKNQ&DNEC5t*}9Y&Yp3nn<67@9 z`~4u#Ci^;hN&+e%kH?cSD@|4iR+DTQR4a^gUmc+88{y0LxrhQ}L|M7V=GF?@<)B(n z%YHZ2M)r*bV{Q@gjNXpYxX#U@sOfi?28;go&Ssjs0<=fd&C zurDRyHD6HM1Mn=ey#wish^!$}WWpQ^arLF*y%A zQpkm(SnAZ2hHrnBWBnb|K^q%spJc)1mv@uirCOM{pNc;p5@dA1%o4ivW(xR-Db|6D z=xYfy1_Z<^R|QYS@Gu9e4VPa^JiGfRap9c?lffAKBU30=XwI9Lv%6SOxu}JRS z2KUOseO1`@o&on@8pnMz;GSiaCc`4Sej$Q;n{h0C8{CUUa&HFQ>k{p7_lUk7m;v|l zvFGi6P%nz2l{WWna8C)@x4}Iq@h)gy?s?F$9w}J4L-=ApmNU&5Sv?Ex9a41J9jij% zUN?e!OWJ6cFa++MA+Yne1cOd5RENO5kYVR6xX0VO_%^tQeG~XQxcAVoph+~#?a}`) zZqF+GJGVEC++LmVv;T(MlM3!|d!77}=6GXqh}#?g2e;?>FS)&jm;R639{M<_?!K_t zX8oE*a!;5t{|>pQToxkthMAJunBzmTvY1(NPc!#bME8)~s|`2S(eTioENnWO)W;0R zJ7y8R^xN#-A43XIg!gW@({dNDnxcK zjh9IJXW_jzDdi(O9+}&p+0$mophyN~-7sQRo*iMEUyY+?87Nz*D}X^gE9nzHQOUQsz|jxMrL!zwZg&XfD-Fz78RA^G=!kUYHq3`CpIz6;Ak5cAg;2h6 zdLqQ~&7ypJD&+%;B!gTLYvV~`&c2i}fGl6hl`Q&C`72=&UPLD>>n`SVcX{i{tojY%ePv` zsUmi%A`Yt_P^psMRYfRO5i>B~4mty2zB&n)7EzaE*&N3WWbi5kyLuJO`L8S$E3Lf> zk3?C^vuwCc^PPq}G#>)QcW6G`y+iZi?j4#BcSAH^l>lkJ67dYpS4-z*@E=}thvo}V zR?5}D2EcE*xKwhR=34=`X}%tqrTJ*tOgim9(|kBLpQZUIxJ~oXFiZ0_Q{Qp7X+94W zfIl_{_BZh?0c9s8y_^OcA=Te%kW0h=96W#>G&p5k%emUn!9&l!`(^Mz;qgN6$8Q%x z*0ZrW3T3z)-oYNPt$6z!1Vt}FQWF^}F zB+Umf*CdIQsU0=OaB51Dd5RN-!?IK>E*9N5OtB~_+s-E71RSQhMxw0=53@w_)h{H7u+ER*HX+3G59IY4*!K1eDII|8)DG@r#r;p zy8nR~JpZr6;Q21tvwH>^+>PjCQbH~@Lk!x&Zxe$wA5%HmZPmnryr6iT7Teq*UXYss z23^s=gIQp3h8H|4nd4Qtvyc}gGr(Yo7n}tKkrzb4RtOlJ;RTQV9T*Jpf{*S90fPib zDqIK{4Do`>7iWOM5HFZ4XP*cGgUAcU|D^&&xY$kWWldZ(HiXgO(~W5K3@`{j|94>U>M+;x#p&C? z;IMbWQ;6dw%n*a@zYv3M|GyD~PyL^WL2m<@0S1v5Y?}cF`GJ_2Ee%oc6XXR$xS(=| z7aYRQxpI~l4B>*scX+`WT(EDJ7o5Qb9kaY(2p24#w~Sza)N3%+Iy@q&m8!YnU{xFB_#7o5QbznbL* zL%3kO&|4DX1w*(XH_Hpo;DS2Q!Eb^GW^lpIysD4izKsgL@#HKkm{<82VuELu{WB(5 zFP!*iOt25y?_h#v$^3s{f(w`PXE4Dunv|-NFu3P7Fc^Xbhmn&A-3CM2=@2q_3X#EQlAZ~Xg8yI!N0A2% zX{LvNlN5pm|1#ozVI*|e3^iDVg6Yt9n;OIihx5AMlvjNa;s$^8%nUe~*Rx*Uisax2 z|3C+k9Q^rhbg=2#3^|DC;PM%C5Xr&k?x2H9g(ypyMF&IV;8y|f-vX!-z6}lzgvdb- z!9kcI2XBLe&joIigEQdZgEQpdEI7D+h8&y)2Rqwv_8OYEc97gf^Jc(7Kyt8uQRAn# zxxv37IQZ|g+~6-h-q`r5aF!d)Z$xnL4mVhN8@jm74UUJv!C7uFKLifWaDyZS4&LSl z7a}+qnBfLF1P5oiK}!#UgMa4+zejNJHaBRW0S9NfK{V*N4c#5I6`Sa&Q(Lw9k@*v*4hn7g8AQ3^=HZbI)UDz(M5gCs-^*8UgNp!9!34T}EnQ-cHl z7pcKV!)K_$rhlad2NI#^pQ*vMIu{xK-&2EYQO)yrYS3|q8m#@FsKJf-v}{Z3e@hLn z{cowkP0T-0gFSzz2A^R5K@IBv3u>_HU#P)hRNMZy)Zi9i{&&q68ZIu>l6fd;Ko$Oz9Uwq;!ZJ2WT@Lul~XE$>#qT9g^jB7;Z_@@^x8o)3!> zZL`QA$_9jYkU@0|JdDGpiLTw5H46KM^%Qx5F+!D8k~U!s|V)YHx)cU>OM`< zg+R9=>kK=1VFn(&%?`eQ8y*ae(L(HC0CkGEOn3c1jJ*w9ljq&`|2wajyb`#sga82{ zxp-*_D;+3NY0K@EAgvTx36f6QF%pnYYU!j(D=H-*Y~`lxHdSh|V?tYc8#h+%q%-T7 zpv9G0_7@#p+2-8@rAjHAsHmx6o?p;z_w#(7|MNT_Ee%kydArW@{2j;lI4meDW^vuO zzWFtCB`}W2-`tF0e13es8_~hr!EXg`&R!WubZ|UK33_ghBRUxTboPor_~Gu&;Ge|r z&s^Dg_mAIQ;}(Kn4Bi}{ADJ6RbTA;^9G^b~cyN4vc&`5j{*Wa{Vgp%p zW_OSWbYE1s-}$Zk`pp||ga`Sa4WzQ?=psDWbKQgN;JzR{n12r*T+l#$5FXq^)iz6= zXNDt>VE=an#)yXwNA|`1J(JGYWYHC=Ihxz4v^l|7O;_y8t4bhGSG|1W^nK>GkA>(L1yqI>(aTLogCKm*6~idjjDnXb^$RGxP`yRqcL}`J*+RK z`vEq^dps;i3~mhqgNwwVRy}F>fjvkJI&q$hz+moW=BfMkyLj~4vohe2HMpS0lkU0m=UEvO5< zxrhq}Nx9uf%00bE3+}!L3w|1;1Z)2Z3x2pr3(j0w7lZ{D`NMVpgaxM;X~BQOg69`$ z!N|L?;Jb^o;6Gu(xx2LBH?ZKPMOyGKEEsy17Q71!y2IWZu^}t?SW?nqbcd}wwulOL zM(84cxN(Mg;w~yk_WU=Vh|UE(OKSdQ9xX9kro8R1v{yRRK<)|*mxds!F#me zpnH;-7imGNh3b_@1!+NR%q}B5Ip&iDVL^G`%zbxZL8=$;#v&|8W?O2geA0oepl``2 znKMhiM!Cw%?m>yrq_|!Iw+mXr{z(gJ{r6}=IA^#^3&P58Xh8!YEeOj|LqlHhA3$ySSht$O|GaNJo>DOm5*;0C_>G2kUg6_NW(e!PX!z_(3gXfLXK13kGq)@>B`( zf{VDIsAn^8;BgV{%|Tueal!VeNk_*bFBrrH+4dkW7{mn$kG2(Dj~8*lm_=St?e&p8 zX5T^CBxaNg6T6 zv70$GpG3{~3-5*;QjVpM%?>|)^ud+mND7YjP5Q%R*Ni37>QT^-6G5(QAbpQk(_^Ej z1~OdlO2*AMe55Wlom@Phyrg6-9l=0X5De^(Z>=~p)31Jh9J#>$7xXnb2nFg((t=E2 z(OoFeZ#`GSZSo86-Gu_Pm;~0P>FT7!PHIlQ5HEv=TnmK@S3M8&qaPx{_5F=t(vrSM zt_{Xe>#MtY7=M~EN^@dW&jqQ#G57+Bz=AQDcOw%R-VM^6MJDi;*_0yQ++|`rBWFM{ z>#YA?@O+&TB$8Fq*|eA)#N(=jfwEbMSu>lakVnosH_Yx#v5Wpt7jkZeA5!^}wUVSh*OVSgwi zDvFB1@HB#dG-dWIqJNh&H)LM_Q?jry9``QI{F7kiXtB3ezeOV1vhu*f7fqw%kqjGf>!hhA*-N^oZ|CbeDf3zZP zDm;T-dJq2VkJQesKK2d#S9i*^ftg!f_oM!Od$YLgth8U=ga0z>me0L2nm*RFz?mUP z{x!EOqJL=fSl7^SEyM&xCNp*e{1W|`$o|m=vgsT6Z=m@e{MQrK`nb72(R|H(t*i#T zk32Y(_6_{kpU|p8_^gXG^N@B39+H*^&*Z|U*N9%|jf6-bc$>yi1t&ZWE^ zXOaGcF|L`v3;%tM^j~ck(tl4P{AcS73BrFt_OF>IPqcdPqJPMtzQTqh{Kw+)Q11AK z{zLe$=NtO3`|yg*h?3r=|HwGFC(!k*2KZO4d-lhK#{uBR2{~`R>Yx#!$L-_A0 zfI2hMf7i44u%d6^KY{Z{Twa9#kZ15`gI_Y$hkgApp3@|26m!0Gx^r;(rR8J)+O>P8Q%OO&jF@5dZti!8UHOO81_~ zK>iPPuw_`wh5X+l0C?>*QeHs-P`i6C!bAlK07jVyeqzQ$*cnu~1a47Hkp$5{6M9M3 zqT%4f8_fg-np&oBbpZ7kl)QXK1kA%+*bIOHBqn8Z#!QU&F%>oUZEW*;KjzRQr!XGS z&`Acom}s45h*hsykt{bjDOW;F5|wSv1qZ>eFK?Zt1Cgs*;4bWU&_VMTTM$lNjkI5J z7Sf+0^$qSfhrC}9_sdR)#~XjRateva8I1p${?Ofl@+x z7wGs0z@@NY-bH+ml-5bQ1ZlfGhy?PIrte&hRpGo~VRPHfj}{?DdJ1i613{**H-1i& zq)Ue@wdW1pBvhux;DgZGIV>YE92F5AQhW8pV@RaLTJKG$HK5@Do*0oBS`^=9VV8|@ zc~{6GTgNyt%4$MSq%XBmgR(OvTxaw<#o&~PDrjZg7ZzYb?*Yl0pg737`GUWS7&=n| z*gc?Z;6C=w$-_?u_8DXLT7o3zHL=zSkd_*Bj7AT|H+5nA$AKK^(=OQ2voW|Xuq)gp z8!~<9UVzy_7ZezAD+9aspXad%6Z}K$p@~%(-7uRA9_Gx%Dlx>vB+kWZ57~3YS4>=P zTJDuO4Npz6w%wQh%R#cpnF~{dNkmv^REq|T188`f_0hz|U^M*xVZLeen0nQxdNr~Y zFiH6`>G}c|{9FTN6<)KY53REalLC8@rh7E6<-CXBUYpw2MwJ%K6S+uP>*r&c>fC%; zHb#+nnZVBqv1m#K*%-`bg*(U9S;|e~8M$)HhUioSKDY^~5MPuErXnM^dS}SGZ0Q4!p0i*- zSQBiFSz1oOV%Kp??LM_5EP;W{5ZvfxsyH)`(3^|vk$4=OZRzi_gb(CJ*K$a6H0-sr z-AYn`H0pp|?AFsg`$@ z#Y63>Ia~u$dG8x=hUe$BbVo!TC-8IF^lc2I4;`<8R|JDo^lJc@=MCxbkq(?Pv=@@B zs2*m9s8+8@V&7%qD3k|H<{zWtjF%M#xTE$vqJM$-4;W&>6IqMbtpl$Re;8ZEv}ci? z5w-#8RZj)?`nIH7|H(KEee#*vPj`i|t>yaN74bhTN~x!TClo|j~V z_#1rC7@qABnKp$poc3In!v-@Yb8Wa2JZ;Ehu-fM*I#e0DiTctUn#Ey3zT>w&0zU42 zyeT|mEKa*gnZyoq9sIItIUM3iHy>%oP>{U2m(P{4@FELc*Q;TgQnp(9=*9ySwP~;7 zL^NDzW~Z>$qd0mnClQ05j#0#eHrmhalzh60z+*UjD&XO5Eesiw^4?+%@ybVCuqz~- zi@9h>$gATTMMI8sIJ4rUN{n=wD+MtX#v~g6Mp?t9P*e zZD47$Q?|CW@ESHZLI!(96?1FFUWf8xg{)Y6rtDGi*e)AYN-_a5=_W%%WiOK+BM!KP?Rjt_P8EtTS5(K zOZdwJk901>3t{3zt`;pmf>N(r5zLjIW1qI5%LGYw*mO5w`ZC=AXw|}w(+39t*=_>{ zJo+#P!Zq;%<+2XAZx6lx@6A6gf$%B1hr6p z-^u8K_>V|5b`t@V$LaV+TR3yVM=Ah(gf8}xa$uB#hVp2-xgW;y+>}~OrF!|#Hazgy zLc%CTWf&e?C{KHA0YYH4-T)ST6w>%?Kfy-Q?q*_`CGUC>xEP}I%rn&PYFm`RLy+1y{ zMIXSfCd4Q}(G&}*Tk)@tZfdNCIjsdtFny8*s=OIQcNlC2txFHgbnb|(LCmLW_r$1Z zX9HcM>CjBOG`|kVwtGc&o3LVLc_U^eIqNA#GZP`qO2kJE;aFsjkAm9af3#8L#03NT zGU0s0R0NcS3b5QV`RJQIn0h8hW`Zb3oYWR>m1Gd^`SXT%vaNXUj*QM83x%i*OmRz@ zprasVmsZlonWWZ!zWjTyT3`Oz#(5u4esn*|jQ$ zdp@o)3DV4yieK2#IUFU1plgps7+)?kE;+Fwn1KAC^~X2d2i3V>dRDkep*>Yq zSi#3E7*f*@JZq{<-&ASdzXpybQO#Fa9fUmOnkylVwX&*Yswzq**1kgJ)h1JdR7j$R zByz(iH95^hbi{E62NlAIAYj>}k6M*W-v7hHRR{{_ z`E}B!o*p#QdvRtpqyEtw7>RufqJ)*sDwW{6;D0nn5}V$jK7Aqfu?JPG9DAAAEZuKR zxKczwwb@4O+4K^nw*tcyXcvEm8Dz8p^ZhkKmv3pH=)kk+PCruMjO2`|{3A!;Mm~S= zJ<;>}Qou3>r}O9E+1~qac9t}xPI2l z(~xJ6X9l8Uqq8DoHCd6}oH=y9`lFAgh4hGa&ZaqTh5qMs)@7NRk)_a*^lDCYJ^_gmI0jw- zE%wT21l+)^!g=0z+9`TG)9u?Ow%^whAv8;T>vKrJ7)79{*(A*o3|};mpGn5kzmU^C zpAQ?f-;?ylzn+6zf=|5;a$Et`7ynEhh;~MTNk2=RO`nO|#Z>8f&Vacqx=!J(+H6T3 z_QrE{Y7cj|0V+~lk~d%CAwP1IA3%zzll$>bDZA_nUOVAvcy7U0a#$#u8iUXK=w9zC z(Z3`~zW{PQ6&MM;F)v|OXkEc7BiVfwtpyPYFn=d61=OdL+Ox?;w zWpUi)3sKwXR^K@BKAs2uDh(-YTxe0fk;P_Wz6{p&UNgbOce`P%uve0^qKco3$X8#p zYediFf8eHmt<2Pia3qFYDl;R;@{4|D1K)oT=0DCWdayyhfuvw+t}LT2@>k1lt?+h8 z8n;l|6b)!nVA(RrAUv8ly#;xqzv7zS9~vW0Lnh``jP_Jpy96lS7wAyNat1wSQ2vxT zzszTol+dRCyxiXX=|2724{{-|Nia*9LLlSKSn%H9vEBN z#zT=&-DHRqJ(tpDC5vtyiRdA@ z@l*$&ONr0tQV7gVW0*9F6EdN!S0(@z-b!JXbV4#dF`MO(7^_vTbR@>5nIV5TP((3V z)J#OXIzAY0EFGoB)HjgM=32r(=nMaFcUfGg)gt4me)4g_4Ee)UpykRmJ6s*9zfLd zLZsDrg@<=&B!q6wks8gI`V*|=3Y6feOZ`Xjs#!4-D6-$NpTmd!xvSn!0!71c1Uqj7 z8546@&o)>+^1DFMWZVcx*>{)6(Qh1A$;-q+Llce$iOfu}QtFwX8RqSPi4k(L)BdX; zBIM~q^)&Oo=)?neFePn3!Eb@mqTy( zKqO(dOu?2DnJ&ezS=d57QVI@po~K z6Phggu5cAJ?A{QkD-Z`+TA2*i@(yy6qFZGQPOW6+aWVsCkF%ebk*G4f4cp=f=80qU zlq?=mq*XxC(*~zSKX{%vA=w_+#&H-WUXZFcl*N&LY}Z}xuwzy<$C_akXdU(T6N|sNM&P`grz&bVZK38GnM0TQvaHc8L^5o%vKm zAanEX0SYqJeGTa)eAmxK&wvgK1aw@=rCpvFl6Q*0E0;#)dq=U1%vq?wX)KS3Q<4>S z*N-7|Nl)q zn0qc);nl_b(-Nd<``guh3bFWs(^Ts6iJFjie6>{VPp_oYzVi_OB)HO^Clc?&;8%p+ z8BEL3T+hNOanW-<#*p&?d775{KnrNGFyMB^jTZrNz-9_!94R*L6B~3NzX;Va*xZ3? zSCMy@=&4myo3CjBJ_4Q{pqisu8ZvJ_jOW8&W9W-3PRFPYR$ZUN6J3W1c#Eh0uL zuNM~rrdv3D{tMzK=9|5tH>4|AO^b-p{pX|h;A2%V1JC!vHOvYbWd>{uqmMj0ABZ%( zPX&q^q|U-Dk|(w-Oqvn^B{*gf>OWQi8&dx_qO0~!lKLi6ZD&l9LCU?f!B#L}6WtD) z&Ivu>CDm`^q$Zl@go4%m3iEm^n~3#$iXTEG+r<(a)!y=LZI*U67v`4q+OfPq==VS zVoK+&y1lepc02686~R)R3vx`|iRIJDSskx6na7@#!O>laF>c4xUI!P%*w;iL@L3Io zyrv#V__Ja)VJ+5)f0hzvucA55B?sjUGM1ea&W~p7E>^69mjtbAK zRd@U0nJie*?-+=(B$8}7plmS@}zjm}=kq>*EyFb^UE zB2K@41e~9yLq61^K{AzVe+I)~I-VR(J2nGdIs)xnG=ZoYbS{6oO{MBWqsIup+=5Pg z?sF)2paJQA370*bqOP?i@j8=v>M$ zr}9vlH1cUZQO@&JL%X7{m^@{pwXIRK_OuCS=cn5%725d#*EfsQBH{h8u)8)#c<9dl zGSR(vMEY3(cTN+!P;*C=oUGfZB02_qiJ5dXe>yYrjp$FTP|OcB#FopU9EV58*%JxV zbUJuns^{L%QB8ZXb@m+!0}CC|b(jm*WDhwRUCccbj~L>vopj7V3(IPH?Wt5mGy8S} zY*1ku9xpop+UYlUx0C%Z){A>Q9COMr2Q6T41EC)rZSZpp-pPt64~a%2CR zYL7XTj1wy)1m`N`*a(~z8`FxPEQbg_@5NB?>`#Rc#9Y>gDK-lT1_{I}eQk`E zJM$#cX)jhQV}5Hm5;7ae7)uA9`~Msa?7f}JIH&LdbznKMm&gx`zYFXOHr&QyXY|S_ zwT}A(3vw<=9=X*yE76A4`tevV@pdpLTR04!Z-Ab4e|aApK}aH4LGQ$5;;7Zz@n=tA zZ(3s^>e?|xXr&*EgH#-?EKP+ethci=O==%D z?~qNEQD#;jA|hJcL<$+syxkXh0HXh%V_Ajq$N2UH>X9|)ymHJcTZKevmjf&~)bN8f zpD)ifXYx6&=}NR)ng<{~-HBa3Ee^VLH^o(Vp*m;mke7;YP}P73|6PDJg+HaL&F7)s z{844vWq``fShZ8pZ7TR^@6Q}swLSruO&bGRo*p-J^+hS%D4rLR!ch8AV6Ih}D3Yu6FwCkM2ool?Tbj+GTL4sjIzs)N!ds@lP# zy4}0^p&=;3r_zyj;HiDC47J$awq~q*T(V~r%$d$#Vx!iGK@cBu$xH=v zCZ}L>K5c@fHq&mq>F_0!(`<6)3rMzVK zWi{;e(Yv$b(Qn6Mk`Y+Ecw)d=Jq$}{FZMtJHe-OXc|Y+#(St2vvM!8kK&Ny%Ks$6V zG$(n?yt+e{qo{z_di#Et_3{Gs=c*{u0A}1zzAS3@7%-|7$8R))>vuv8=uKgM?o9VO zg3g(IIQvV%1onRe|2Qw+5dwY9x*Ms`$@?RhZ8iKUx@?9JoyDd)Y%xrwbcutUtkm!mdOllxB5`QkT3iX%UUn25-AU1 zcpMjb_E09p7rnVjzjqZ@uuJ~)h0dLHO1VH@F{>-p2XdioXVRNWfB2jO>LPH4nWD1H zJN8(1?#hE+jCH$jnA6%yVWrlT$u$J3BMJE%waoZA&R#}RWuJ*DU>y!8-vXDc_P$PL zhS`EmkRcaxu2?&BAV6uDh*SCo?Eg`&#=@BIF`rnY>f+F=fvTR9?IZq2S=EOXm1}h zjuhC!iDp%>l~-ydv0QgC4AQdY%3{t??M$d9Gm$E;nx6v-XQJ3B1LZ~v3@T2xZk0Mj zC2}f`S+|N&h*z~RC5K?9{Mz8gM9K?0iEN!?l4uK{pRO_Pe2!WtLfw6yx%A%^*tXZm z+9Or@RQxzl-~EI#7C&^Nr((!ev~k2@*3#nzDkNT$73Xf(&>ejc0KL*UCv z>Y(l8<*loSGg^=1xni20gGUId;m%y3!8E#ixJFUToq6sNoZ8I5g%W3%qXEhmwcI-H zJI|JI1gwUYAu)nX8WMv`4wZwB&ty@FDF;n8C-U!&lmLgGsyPWun<9vBJ7L0h5>3l0(*ydQAlS z)|X|1^ZQh$AaSm5_y|0MD*w!B&AyB;kh1t z6$dpqnbt}824>SQlGCwB?qL@kuVyCoRT0xTh0}3Xx4t_r8PLH`TC*Z*nSu5P`Y`>! zT&5+hL%C!imo%y&`~`NB*rzDTFmCcA`ql9zq{~ofNFIp3iDmU-0ZuyLaA7Iz?8P7lA@M}CAUS#<+RzCNOPbGyZ%(4~ zd99W{st9jYzOK%N6Q#%UQ8)akj!H?yR8lH8w81QZuZQT9Q$v}@Pr?IW?F96wqzRJQ zjO=D`9042G9FLJ6d+;5N14)@s2Ts&8F`9^-j<1ErT4=B&mmW(Ku$? z0K|tT;WEaVFXUqXK`I&H<|e6F@?U73VbXyVW3k$@V)ASIcR;;mdpK_AAY^HMCQf<` zjnQ7bn~Xz|u!guU=RyB@jC1ofKqtnO2lr1S>Mt7N+5n}in-T{2eG%X}r&2h#{E@SE zf z_v;c`_jZivzZ{~k;f4;*Wd63_ajmeEv|tBXr(6RIn(wL*3AV?1TS%kt8o1U4@MMkd z0cQj{0W{QOM(9@xz~spc;n4A5RW)YnB`2kslhTw_60AwO((@_ZENxahTBP0byyxhS zSho~Vz`88ahzlr`*2SA)z?xHu4~;n-n*^AT6I;7w*z(rIf$IoNPvAA+{?nP{tYrg? zvX*(vxkpv-_BO8m3UxJ!WQ6$<0({sGA{v%_pVDD9jMA$L+}5fz-67I!T$# z$h20mC<4O^GJ``>v2DXKE%%NkaWJ~UFc^(XBH3h5#kZ${6%P-^%)Hp2nzleHqI~1b zhmRpq?~5#Xj23$sWeI=s899V6Xm|Xkd0QPdsDYy)ZOKR2J>LgcZPbly8XA)KsYv~A zW~jZru#*~%vVx2UvM!TN;x(91C-B+X@^J-5=u~PHR{2}q602s8LY<~8WR}YAh59U5t4Z|VIxGz4XS7;qL~G%9_N4~m^RfA zUTjFqQuRf4!cn*8H6!XPT9GqI76S2N9|gQBKT z;4>}VH>75{juxWgzsn0bSN__=nG3f3edlX^s`s<&DKhQbhgeKcGF9|7brX3M=csI; zA}l;sGMqMi<;#Zqip}O?bEUdiT~WAkhsKuf8;Y};SKG|h{9nvAKEh0F!r0PGsz>-b z5fhEo6X_7&4HHl15RoK?xoB`I( z$*&W_@?w}6og*}ans1>i((tiEz%wk&&f=cW=y37M&dp*wo!qSyZ8P*u%Un9C1H4|i ziGY!h`P`0wi2hKEoODx~(>yQdPn0PMFdZNj{oyL>x&`c*HovEXZX0FB#u-}68A{-= ziAF)T0^Y1!(uMKu%^X~R`864O^;=CeW(&6aW1$ZIhF!o43C2;4gw)|a&1re44ocwh zSo576Sepw!e+NUzy(ZiaQ1%2EHFGu^hs)>>GSW-|?&{xMx(q0@Bodt@q&CXEFFC#tz?>kH2LItFlaA1|9LE|h&7t7G%z{C4qOh06< zw?L=FKg`XlUR$;tG>5J=>TkUa1A%?Fza?V%bq-ep<{Hr#{K)-$E_geOd%Q+;s#BW* z|KD{m5m5pWd6RTBnssA28<-t7(QE^}#N9q7au>qOI+ud(!&8T7$*(!af5bWkTU z4{RgVVjG(zb^s-RG)qkbCi%v*)Qr3?{scf_X!i_xr-!0GPF$%-;u3o3`lD!KWc#3g zYsqq+%4p#xi{)UB^!dmn$&c@gN!)>zim~v;P}mnHc-O|$pKm4APVP~%eeE1+n3eRf zGNDLI*K*WGVP&y4A=@VV+#&Cl6!|(MLb#Dz?+MiXadib$ZtOc1z*2|R+318aXTilD z$tqwseZ>lVHS?B@)T02ROwtOo6nXaYS%J-jwjuH?VkWstahVc_>=gVZHA z4_uvQF){YbFsIu0V_@80eH;AK-+~Y$DuDhe{BLe~v$d+$c1d1=Gt2h`!c5LA?!tGS zSr?ynX2E2sGwX|Tym9FV&a8>BX!KUas9=eEs|p393%kJe#evms_(D&)FYe26!^KG2 z?F$=~R>Ob~EsCyFeXr8v{(JoZxhi1v!beST%Tlc@H7|kL?&!<4d@G6_+ODS*JmQ@LI&oZrrdHp zI*vH^&2H>xU}gxr+Ko)b1*~PSX73brtZAYB{=vXLn{L;I@46$G92$x#Rv+K6+zqV6 zqjqAnU}(P@FJj9x;r?pslFg^aWcT(^)Rq0bAJ1?2rp9S$Q6?utY~I8*5nA*o7f-6@ z$Uv=7WtKY-Cf>oYF{$2z&9K@UV*6fiEc6-a772A|I}X_CC_cm%E|!$LGeh3;DEd?K zaDYA-ZO_(0Mjua(M^usBy&)H3voqE9bihde-W+JgT~L%}S2SHO#!B$SZ9juGUVf=7{01a{;f#DPEx0Xwk51LSmKE(W2xUh0(2gHRz45pvz?<zBv_#vYi5&#=Ji+(J|9UR=^lo{L$JI(|ng5cBRjotEDc_0TO*4N~kuLO$lD74CKC)HhY zSmh;LwSb|0Pn2Y4aXoPt-33lJXi=D)R5v%MU*H9(e#-{792A+RjJE@QA7dk0Rl#}| zP{b&mgrDd0!>J?r^EuEU6~ukg1zBaN0sT%Tx*r|4g?0;I%tF5$8avySsoP$elV~|# zr5W4e`_`vNbwqXbtx1ab{795>l`(V^m~zxD27r_S78R9*8v)bthX}qSRiujL-bEcs zF(y6!n=F{i8t|%(uIIBj%q}i5)7rb# zuBh+Fa#vBSjfHXTiJor_s9hG+q)8U{q%Xt?{VQNZ(&IHkl`{63h47?^XWKNtGs49N zs9O@>E}cl?dOM_VFG%WD(lvi~N!o{{bK@bN$nw=-QVElfnkFSTdNcF7Q$Ui}5K+P9 zCqq<`JV`#+u9KUdi)Fw4eJE;)xTs?WNy<>+(J!iC(g@ofno>yd8$>u-$?I6?nj8h~ zFq_VQ`E6-*6?7el_MXHSW^a`D2N+cZ=#7(R_-9)2LKMCOwga~Dzg@8>?zbSm3m<{8L0R8mU@g)y~*cfP1@NS~MyKlqxg9C#e% zKEjPlIWTx`XW^Xr&97%yoL-BZl^yrJwAQeY=rPI(XYWjq<@nebHOldKF5z<8pr+^F zD>T>Mzl+{vI6_x)RFXV?J!jc|nK-o1#u;P+JwR&XbZUcT37EQ}-OyP;&9Ahdqtm5$ z$WqFVgo|>@fF4M{wfS$XI!V;a3tjigsfkk5*pDd0`=LnDQQ(?%{$$eGI_rGJRtHtp zdvW2SKb}p>Il2iuw2u@ zbcT1J4{h$dawsk+(i*f{gY`){S86tW2Njnm^H@orhr^DdU_}wB`fA0Rs=u$wg{miJ zz^8)wpNrw1gj~ppg^81RQwPII&9S2MtJt*HF?qvBN6m%Bq{MqP$NlKaXATVr(}6x0 zH>QKPb|pL2!=ZVz+(ae_k$>`@n+pjH)>MWTwUDE;utG=f+&mj9%Oua)fF31fXri$7 z(!Sq=2gm0>Gzay_rz#l867@=W?y%Y?NQ${K`w_?OmBnAS+G{u#|RN7w_SabMU`vTjr zEqvW1ht$H@lG4sAlDF9S2pHY%IBr7VP^BUYjBq4Z~%>JT+mLhWQUnOv0- z_#z{FW>Ae)T|*Ix*ap^QfYBbVn_li{*yflYjx=+yGmU5_hFD&$MJN5r08bUuWK!+i zZ;WUY{gZ0)`tc*|D@4iceXr0k4rTM>YMn-)I};W(Q{nSyLEZF zZY{UV<3E3B3)+ zPgJtb)UfVTL##n3c`slrnnEtF5BJ(uiA}i@=?km`K2%qAx)Qj~(=zGo-#C=mnyo5~{>F_Or?~j2V{u;NZ`5;fwT;_#}FgPKijdfW~$4B+#x4 zduW&MZM-4yNJ;M1xzn9}-{=Ln5$wK%o~>37)OZN+w#V ze+;S}nsSjL%I$~uN1wo}_p0@f-g+!Riagv9uz7Y54k^(h9rO!>RttpN85hb?&&3&x z#EM;S_-JeyBNIGK9AUa8DWi;^B%+}rM?K7)T(~g~VQw0g+3;5z!lnJDYxQ{M!uN3X z7V5x$u)U{WZsogWWBj;q#h(j$4W4*HuxT_BNw8WnAr(rE-b(`6rc`r)rm1zk#86;> zlq&uonFNpSzM@}=9!Cd(wN}~g&m8kXC*T**Mjl4Jq4`WuwQFd`S;i&|>^8*QCwiKt zCIedj7+Xknn-MDXz7ec49lwaZDeL6mN0iDpBK;XFt6XyNyJEEeD+1$&=#c=IK~gTA zHH?HBM7cdqgT}5c@JXOAx4@nYh8BDRJ`t}D^7IBXbk%T8M$#9(Ux~I>>Qi~z1E?ZX z%=2A^2aM#oOxjxzOKJKkb0ytSN$1-J;lun;hZe1mg7%Ppt$1~yTj>+ewM!_u9Gz!_ zl7D|zyd5aI&7e!Ry?~o<2ctbsZRFayb4lE@U4=vmL}}({IqO0ayzzU{qX*5i0;E$O zZn$R2o%hbBbFF(=&?HeV2e8q?vp4~oxPUKCuxnf|qmj;`a#owWve3P61X*&(QcWi; z>jQg1Gc?Yj>{(bXIsE(79*E_&`Y*Ak#wg9S$0ZB)xMhG_yg9Sd3@2=Gp}2-nl@Lv0 zLrt)uLgg%E|I}5e2RuSL3&cDTP|Dfe7+sIEzD?v-peVj26h!LEYo#N^yTb#!j|YmD z;oNx(P^Lyy-NHE4HVi)X+*xz?sdscfj`0MFyA84nd}Z>UaTv4CQUJ@aaY7`R#He3U|3Qty84LBl98=|x1${ba;{3P)FHKj# ztmGI*4qUiI-hVWQ^yHYk5|&-KO2TJ2>5O<-za!=a`<=Eli8w~+K`57<+F!x$l)O1f zC#B3qJ0a;Yv}agXC1MOc%J7(2k4K1u=D*=YtF4a!kcO}scO^-|x?O?vGH9+}(mSs%TQ$*U?z*Jo^$t3F5tC)J~@rJ$mc z(%VTjX`vd;5-}4`f@k-F0uc0^#F|t{zVn({9=ZNzz{E&Uz@M~!OOj%sVg9w>W_ShJ3czt6hhdSMiMV$1d4Pr`M_uYjYv$RQ-A+Z}dX|c*fSpEQxj!{7NJ>vWGVRmRjeY!26ZS{H zj@vEs9JRr-AT8186hb%USprM)ENJ`z5xq&N;WvHKodqiLRFAxvJ&lbuDpORSRXu8G zhreJ*VqfGY(H$j(Er^qNui-evJZop?14Ucob5(7cEaEkDdnMt@Pbor9?+qP@{u|Ct zM3n;CL-Yd~)0Hen>H9&-JRRgP3(=hpoD!|d zBJ%uV_$@4H-Ps27X05COO>JA_fn=hDzZGpzgVKh`1#afr?`I7;>amy{%Z)Ld3%d2; znIt_Q+}UU!M3po{kawA7qNf~#;vw4rwb{dv&`fn*YX*y5^prYOjj3j+;z=$z#z6Zd zww?F#*4RES)(ppr`5N>*4crNkj7vS}Qu2#~R8x5!T8NbK7>`~@AF7A&O+DD>Zq2*GJ!259}Fi--?2V4H+}19*nw%$qUrn|V&UG7J2=Xi=7I=EGCWf~JZ)StYuE z=P$QeyU{IDCPC^jpBL*S?;lAH?6bds&=^-QR+B!?zFfssq~j@I3p%)5oIPHwmDMZS z_?V^kcwlOe@Yj^*xf2I#SH1QdcU|d=JE=F%LlbN3!^Z?2l1J$h`JtP`;O`{2j0*R) zJ~Gz~TNO@^!l8o$lyJQWYM%DHm>{zxzWfoSZJ!#zCux^3U<7o?7|M|siFVqcgLS0*c)#j1>phFKdR}Dvhbq+8o34iqYtZT0 z*c@u&!Xb@n?TwQUh1QrLi~G}yCM?C=nma(8&WN+aM+5tOC14N{DJ}ntITM!xXQAUk z@W;dTFqgdyJnk50!aTN4vT!RbGrat)IWysUSSIw*{&ARslT(IKu1-0lzFb2VcKq5c zS2fL!l}_=nnH@~M{%A3MgQjL4oU>7pR%qUD*5qiuXQR8<%i-v4gzuVwg<3achhV@w zOH-~oenEYG2o&opnDMZ_VMHG?vbYmZ<2|BBQ~6a=IHI)AN*ZaMteL({Efp7z#J=-F zZMw4~?48s&rz*sP)ySoI8lG-(FqbLd7Mi)X!);e8BT^}dOD(XXtcwO`0h0%s`Jk*& zj%Elmj&X%8IldgO{^X zKGQ+=Q0@z@w43w+VpEmG*ATqW_|ag z(drcZcz?~3tu=X60qSfCy=*Da+@`82$!Y={yTc?!`IL|;R+lBepyR*PEd5-pib_;-)YV=?>z#ZAJdOe9IlX5=MY&PLSPMsy9uAe6vC zAsj`i66_fLA547*R8wc%_wQyfMZ*$Nk$^_U304$TG@xi**@1-Yov>G!B4H_0mP~i8 zt+?uJwbd$GwN`B%)oQJ+I9jc>#Vszr$M-$oIj`}=1W*aN_qosiH;-!xKWJiYAposQ zOqH9h;ZGt1WHaZJ?P@+coWduv$ZOvjc*Hqm5=DP_z*#cC6fP;AmzYEP23)j$pb+N~ zw*kwf%_Et;S6oi!1+fmQsK$~lIPSvms+kX`v!~w1pxKFV>?_{&W9rVr{r2%60{q=R zXwN@jv)D9rVn9kl$;r1TN9=U_ zwNFa)=#&c=kwL7WP|{FCHIl)yWsoUk9ucoJ4qHjpIm^8XqOqP&j{!BnjZ zWzH3FIRc#3M!0481bOw*TR0rAiS%$+(%R&GzXW%Q`{6GpMZIwKB%YNQo9GpPQPlk) zGJa0o;~AJh;qgw-J3FZy7V)ukc@nTN@EGM27R>eVo$5>q;Yb_mu(K3m$+~oGXTXSM zly|I8;P7B%?C9^03Tlg`Ho9JCJh;v{?^8@4GW6l?5J<*LU9Yf?Rh!7(&+qI+FOL`w zr6I~pM>xGFn$(dbc*8S*<#V^}!J@8* z;}>_~Er@2GK44oQot$N3-|Y;&={+jUW%pV-E)HUhTifpv(>&`6V{Otfv5UuZdV3_( zah)FJ`%fZq3JvW*5)ns8>cllcJ)~3pqn)CQ6wy#G?(#|Y4l)t?ZB7@Z9mymQ&femL z(a00xb{bG6_I7iKY)_tNm3(xPwskl`YrNSpDYTv5fhn}^C;bw46&e1A5T!hZ5-xK8 zQnH^92pa3mL1Tz_{$pf({CEKI)R5_VBFlwK@;HOP*GDWw2toovphHHbYX$9*lkaFM zfXvq<-P?VSn|mEY7(DijE+U##Xiuc(2qsdl{ry5FGCVySpObu8kmUNSG&@%(NfDU5ULb8*;c`8FCU28~!sRZM+DCP`OxpMh3lq6Ho! z9$06IjYqvRyUgXu?_IA+-45D(0!TnKJHC~!cJQ)dw)kf9#ymlbqrc!&+s{<*VMI^$mJ74urnZR(5**7 zo2WK*EN(c#Tlh7F8>ChJF2fFO2QAnibLoM;Q)1XRhK4zhD`z*-hR_EuKFJbC5_u>s zv4GV%H0KCPTK#E9yFBkP`{=`lZkxj;Fg8gfepKwXiLsNm z$CJZKen&g(=Z(xLmg5;AR<%Uvk7F7f9RGwQfQDc?7k-Na;g_@xPx~d0SQ$R^&$wKb zIl@S`eL!tvx^_?V`&#>#^6))3a#9r=X75EW8>y@TQYv z-NrQyAGdVC;z3Ub_qX*`P-F~ibh$MLeau*}li0ZtW9fc1rDQ|e*H=MvW%XR?A2O94 z$c>2pUWdy~K>4{JMSR00Bt$^p4SU5sh zLGA~bSE&Bpe*d<|KEVW@51LEJS#n|>vMV3fp8!5`ko+qWuHG|$m9OD&3f|r zjYLQDTQ}wStBXcgkup0<|HysYd71O$=1LM1PyJ*03?N_RD}ZU1BR zsv>(eHz~X#;aP;OQr2BUX6|H_EDSP65IieB4r3>ZQB+jyrlR@Zde7Lf)1#kgyBiEu?+FaKtu3F#YX@sYDp#{!kmSpkM&P=}xiE zrwC)VnMqU&42S5{72JwGkG}}h!nlHxX!hx$K}6D2hEb=6cKFb*uyKXGwS*N?G$`DU zK%eut0S75p9CZab$O0uq-V^BY;oCUGy3pn2Ns!#>aw^hI!k&I!b5H}fqp800-4 zSw!{*xOkNLV98t$d{#&Ap)-BA5lfVKB3Wb?#|DmJa1Y&O)6NeqCqAe@N8!ZKQQh>> zOK{Mon8^Re_LE~}yDKIU`O*VBqm+R6$4GL;_3qjBHNaFu)mG{zMr_<=mDXm-KA+a znP8&n+(wX{ig}+vtfd{O? z?XibEh`M9kbyk;nr}S8Q(Z{!{kI^@3@8Cd)Rd?Ex!4#R15RB zBf$|Z*y(j{(1okF0|LWm1UV6Sa+5C#r)9l*$)tLMkhO`q)hB@A%E}>XUkZc6{aKq` z@urdG;pK_H3ElFrhb}qb+aowaONwGTsBUHqIFCz6qKF?+E8*D5QciE=+lumJ2t;r)78v1HuFTDY z^aJP}Ag;l6zJ^9@Zp2QZUyxs)&m<|C6oo?1hL`q*`8HS2eE-)0F&i_eFQTG2Pt-fX z-g#`)PHZn5PP_y=LnDJ7TG@egUxD_3HpMPa@Cbe3+q-2jbIQ#j;o(&*yC=8!IlZW7 z3yO3Z)RaiZ9{)cUQVm5y4mp(XIT@uzLES9hZtP+C#8URK8~M~BKj~Np35R5B7gXpf z9zW0i=8?89*n^NzFJJZ$n3y*1x_@8HNc8pX@gizX7RRsHiEOR06}_t?W4L`K1g&wY z8`ZG?2+cq9T<*h@wiqN;PIt^uz<%m zeF7e9_kMW#;r7Jhc7T4j4qC?CK6jX$gsgjZ^7c}LYhpf*?u!W{ROhi6R)woO?V)wk z;7SJl%IJ#0QLK^_r%mSHA`VO=6R6e$I-f4Lzk6dsqxa#6m;!CxKzjdWLrB)fV6n^^uAZhF7ebKVk<3je>CfGL+3Z{oIsxtb7@R3z zbAn$g4D_y7#PCewRS2c;>-^fErV|8_N;KCgv~7~F7GfnGNh!ozw@KnaEF+vKwBC#- zGhHM>kPn<8Kul0_5<6m#fKatP^s&Rnm9Wkohz*YP9mUub8ZV^Pv0kd9hxR!beXJv4 z$1lQLW+JbHI!`9Jsl^`O5a2x@F^zt7fnQP3HmMhVr;}Sr`nHmt*tm&|QF~~Tqcffa zM)WXsp8ng|`LqMfn@-^*UBZ9(V&8mfbvTZaP)iaCReTf~-ERmJznZAP*N`7o+iHMj4_1kThK$2+6dN*<_GtvE3Cc; zXr@g7X_5Ci>q(8{?jv*TCQ`MEIsTNx)uo3?yuT-@{OI)P-i3mlp0NbGd>7rTxtcOJ zMBvNj2;{=PrQUDGuOnFAA%CjsitF6-uJa=4rykjaO;^@|yv7@L2F1a1=>uYvITwNe z+T;5=<{IK3V2Q}N-%Tg+cS+>(!8*?>?LrVyT`weQ4?g*{tOJO4{-42l-huwM7Ef)A zlOu!ZS^^J)k)hk-^N6B{xJ0ueS^o%ns3>+7R;2=H(6+TtMAklO0 zrPNX{ZRwE4ASVoOq&Xaymf*|&wjX2JzUfbx*zL;l_0k16{vn4sZM;YNeGftv=;-GO z+IjELT@K}|WfAVTcS!Pwd-jLBQ6KJZOz_?9ir%0)DkyX`w=WX=*$BKDT(0+Lspv7> z35`Z44=`9Uuge`1s75(`s3DeYju>7K4Vci#V9mwrXtK+R#?6l$sk{39RGz+`3w^2G zJ*j>WRY9f{$LcpnO{k+nd|h!aCB0 zPu>+CNf||Akh)y54iN+VghnDsqIJCpAWBX&xX4*M<=DxhbcB&$y>zM0YsF5KYYqT~O&HPi_y*GxWJ9LPVeUxA{zDlu$iB4LEehEAZ{0 z4;7h^=m-W(j++UM>}%RrOqRp$83fNz34uSRIunrv_5*=e8C*ibfE(QhVom9bjf09> z7AG63hem&s*-S-~Z4HkxB4hdBE_?7W1b!ro0iB=`h=`)M6aavo&- zL!`;~D2FKeJ5Zime8C)b9_;Q>om&obn@UPnLKG58TO1StFPe>&Le2~=fy(IA+j*B` zSs&Jwy|e-IE^}iER&~q}A=n!&(9+vkUp0D>c)_}0XGhe~|LVM)k44pa1zqUT_tys@ zsF!ScqC7VzALj0RM2Qehjr=%55FY)-dWsbtJj+N@QQ0RuNdqMJTo?tylTo1UT7QRB zk&(lj+UB&79`q{xvJ2yF@#Wwy;4r)BvSw=WlX8;Q=!Iu(M6A_Gu3oyaLvUO@~t8MRrr2rElx(B3wM zum{9cyA!FY4-L$16qeG-Iv4KM#hUXraM)u~==geW2DOfLEZp;XuVwu)viYUEdOr7b zW%YUxJ9r4|kmt)s9Ng)zzeMY$oovS2%*%5~3gE*S5~bKd7ja{-XfG+3)2|2M(>WKg=WpU9!U;xV6QVIDoZ1*n;W_K8 zI-Fu84t^3sUE)$qmf&$PmlP2H0qtX)45G@gAt@(mv*A;!@))}yb0br z_Z)ZM$gxZI5$$30J}m*Li0tp%53;{T+!};;1e97$9()knA3qZ8h$V3>bIINmM_G6r z#Ja*-`8u>e{La(<12Jzg_j1r!cs<<0br6ibQBSmpcqzqZXU8QAq594OOZWD_B>>=3 z8cFsUK#tIUP><&lnAXb>35tEZ(`8dEIIN0JjG~?BBwO~|&Ktr|iEWx7{6#DeE*c`f zWOImrESIc*V+O!e4*~>kBAv=TRFCrppUzK49q%LiOy8e0vv*|%k=#TMYi#gX54HnCrcv`4)F7fH8WBWvQ(X2muy%XVSeQiO zHpYx#M<93vd)~||?^vU_1j=U6wq~+1fa1~a7dACu0E6dDl3VFuai2J^*v%uf8Ch{y zcj)x9E~*P91Q;)3$yxaz+`aX=qI)Y|8oIakC6Nbj zvbwjvBaUDYVdGVD90UDVA5b9n8CXwJh&CG8*=fTsq;oq_AREG(n{3j#X0nrY_Eduv z>!#Wa>JP1DNOG6kC@!4Ol~-nJB`WRHl0a; zyDYn&hSJCYBzdt@bhcEt@Q;k98Dq4ZUQRFhft`$=F&hG%8WqmM8UDhNzBlDLBT(H1;6d5C=C_{WsGT`TpiQsG;eyw zPj8ML*n%4e9w?5-MLve-WBI40bDFL|J$!rnHs@;;6lGrDR)mc@fuaG@Y}L1!@3H51 z$LCJJt;xpYHQn8|HFsBF!MFdNTGu`395xLYl{VqQ#BC_gUi4enZso72?8^}eXSb)| zRM^(<$Iy45MzQ?pQ}@RJAn1Q3yb;TJm~KyXTbsz-a@{X%SAt&$)6+}fy?dJGPT-%CXr`xW0>5n zbD7+EoKz3CH`^|_t?&VpYoxOjGdY8YvpL3@Omdgv$?;~jdj_y`cx)IZnL`8ES(!OV z7Dtz5r6i&mjT(Dut}WGwSo^U^jmDgoNi*3jslMr1p;_6X#u+p8I-|yfJcB7c z+m@C}URn69DIv(RO}Vu?JTx*$+cGvR)D?M4^K^P!PFUDjK-dl0-t0mjc4}HSJBOxA zwizp8^tvpT&7jH3&a|b%GGm5DtTAMgBI#@sh}Rq>nRYYPMYv)Z_b^Jtv~KF054DA` z)ZgO^P?zy9oX8MC&7h=IDn(X@LA_Cr4^u0siDZGM!#`|1#$coq*^v%|-}NaJ5n8>+ zYmB47royPd|AQx&$-PAno$Rc=LT6K~n+U&&iyp@Y^?F?RaOk``kMsZM*-;$*KhKH@ z(71_$kiy2v)Q3ZcHK_N)?aBZC@!|e||CRnX!F=1Vao-vN1{1wJ}-8NBw8f*5b&hL zyDM5acQu>Ik}M2eO3XlWHRN6_j$RNeuA5T#M7&xTOeen+>S%RO zqHfid26F3Ybkx!MU0G2qx?5fpyVPamhnGJdOKJ4F+obB~3woWN{GBpqx|(#6$<2{^XjAFzLZXZ%QHq)N zLFDIBL}enAA|Cds$f0J#mGCh!MAC!WXlFJZvtc9o9?|ZpB;aZG0Bq(`pVBunS@e0fce1NsEoFXq6DX5gP88eW z@;`3^>#l}IxgBzbhQ`*=G_)=Ar{+7JE)hO9smC}^RaI3oHWDdD=*F2_dT>VW2;Zid6SO~ z0`Ga3=PXQx*_LX{&UY2^xH=x4{Yv1(g?Q2Mg<>aGtaR=o0b@rn6-o;o&_9sse-k>i ze^`GY$OO11bZsaZ*SP&#F}r^@*mL^dg}x76htT!pd%wwJBGf6=IrMF)57;dBp#E9? z!x1`|t?VDo=72qf?av-g#tpVhziYows4qfi_DA(A`c>$633Uy13w1~60J04k7&-`{ ztWeKT_kJ$~@4+v4i2(F#gNOHz=r<$S58P9I^P;%wqSW4)9VuK9=sxWB^6v6ToybofQ+PLg2x8$z@;Gk9E4zcaL8oRoz=?@-V*FJl12qP z2kU}G!HEb?3LY996&!%z@Zj;m6M{Lr9pi&S*=eIwMk8@Fn;pdd5||Lo4E>MO*f4PS zh~B${d&OM0jns!Xk)ozkQ`n94CVDfSwwm5TZ>6`<(N6E6chc9;v6jA$zMj5;j*aw9 z^v(27>DWU5jQ%-&D;-_*;tPA|d+GZa^!@Y$^e^dOfqsyFi2gPG8_*BakI=uR9|ipw zy_8I$Y>EDBXhR$MmGQ7a>X816C$sU+7nBm75!te)UC}S97I3oaz5rk@F zGYG&R%oxcS#o&N3nlXkkmJvcAf5v#m1ja-#CNUC}V=05j z0G~lFb>tVB@HqoGEDqtPd^p276F8$dn97;Op*VC7264PO!#I8%jO2X88PA!?!D0@Z zGnq4%gXx?(oB&QJ2OLfWCx)|@hrv>M!kpG)5)9LgHN21qH^5##yi8EAO-t$>IH`|J z=5l;i`g~2Zbxb@qnKJZi6c^+uT=A@3U-WGeR;?MV->~efN>nKhs9giA=WS-%zD@_IuZ-PnpZZSIn%;*|>glzyc6cs*IP(!|PjUl(}t(4699K1A9R{xZZv7@@YcdMU7{bNMFLgLol4I3n%6WLr&lB6UA*tXO zJZ*F54Hn~Ho|GTO-^522f1~I>v1=R#$-5K2RP-otF@L3bt>sG#mL%;Ni0+%o)EcDc;Fa&I+4;n#-|_D&$%HM>oN2#Tf+MBx zR~)N7*$VHDD;;NfL;1K<^?TeR*$*S6T^`Ps$5&=nf8LCPoyjR% zJFc(5=Jg(nL3mgBd^+Ga)E*mVfDGRwlsw=9E;=w#`i*~@t-<8KsRQn-n5 zP4t)OjjB_Mak4+t=#MakV%uW&~#U}@+p)D;o zZFlw%JMQGT=k^zDDn!t-2SwA0dyBEoVJ=-?wyz53s<+pcHwagwxuvn?lNM7eF1L;C z7*ZO+!+6O-a|ARO}f?W!44M!_9A<7by zoSA$l8F}gS?3{vK1#l@GTJ(OIxd?xm6q(pZ1k0=K5?E2P z?4;nXXhA&Qt423}Y8sh@sHCacCHY74vAFPuqC=(U%1~UXsfw(*SdWX1ff7fPiic@} zd*T`5Uoz2}^&t1p(l1uwRLA!nmpXEV7^+jR*exw6K#t(DVnIViE2i*nB>vSXRN`Z0 zyJ15?b275qboJW`sxwg8TDr{7n!X%DYjX1w(J(?8S9n){T6}&LdK%2)%F5_`9Or*4 z@D$A>i9ynd@qcI4#6u|8YsxitEpl`gU6*dJ0W*xrrZjVm72c_4dsM-jWmre%-;+AM5Y0!Aen}zRY+m4IQn~)SBF=+r}|98a2rAri!yg)PRfE$ zURqv$(STfZIclo>Yi`%#(kerPy}6(j(su98y!CU1*dX2~E>w@!U|4EHYI)ZCEX*yM zUW)41)eyAucU(Gfgxz=9-_GQ!GJNv{}#EG|A&qU`v^tx-NB!9V>H^ z^X3%XS_Wp(kRo-_o+9`<=#DQOdmNCI)R&Aa6_sI0rLJ;V%_p^ZTl+(Uy)mN+bMoFc zecH0L70mXIj%%GW*FhoLE_x{@4NF&KFZH+55^JHUKeT?aaHa^0B=6$51-XsTuDi5; zUY2Vb(n~z;?=-(^v9V&YIk*H<( zM!s8N(=63vlOf8QmE@9&qV&^^j!fS?jLu8RUu6m7qXrFZ8>N_|rJi@^w;I1unKWC!Xey9DkRrE?-{{L2~i>K^j>N#;w>aX*S-m9#0sl)tO+FQ3xA*H1~o zvHY|7F-1$4<5KaXlEBi2a?G#HtZJyqUWLi^VGWBLlN)iWb#3D@9-og`VYIkeGf1hgDQ;liwb9@MsuCh0*Y;UyRz6A57VN$<1s~q3TXC)j^ zI4Lnm>8Tv5ELWpieM_CLDb}N0e?wnu$Th&(Jit8BoMXmv%O}>r#PCEMPOeOGO_il$ zR>tTIOU7xUcF0xbQTe8PQnz)hz_al4Wr!?lbzCj0DaXPJRprylz-r8@mQ-g|>uT|P z?Wc|Arsd5TvU+W6dz%}NAoe48@w_%6(nUYUPnDgMAy^TiKB;|ggw7IYnV0fqCPAhR z1qaJBYjCiU-5eJ8j9mWOAL%`8tx0&4Az7a5$SlR5Re!WgSMI6DOl1VWhkt+%XDK`3 zrs9DDCp0|WWW#Q9%{BjPU1Q5j!Q+&V?1$`uxlrf-ng3DIof3Rex~*bc)r@LvU$v%w zO2f@YIJbSx>*mo#u*?3Dxhsd-5vAmnxK*F5hoLp4jZ^qjHC)>DjZfpYis9D~DJhNT zsIk>BHMu%nmy4QZw~Es%V;Uf7WHk?Iu5X1p`@2qOl}&|o(nD1l!KMHsT!?cpx6&LI*#IHq*mTxUC zD8aDO$kNX$QmgS}?en_4hTI11Y-(&OYuU91OGV|UMh0{pS; zr=qn*ItLb($;y(;50~Rj#mUO_I;sIF4Q$9Jma zS^r7$e`yHL_N$Iq-JOQ_)e&WX316+i-{J@2`Fu$OcGoA$!%E(%@OQz{`d_WnYw;@c zk&lZl<;%<(v>|HSa8iI;?)6h-RJ^hde)uV7o@H$_nnoN}C9(Ne8w#+|s+ zve}xQCbXIzi3gIV+i|*jl;B=&Xgr2#OxgmoHW|1X0*Z8#QLI?a3N2ia= z`l}T2+X7nqy|pJr@diB0@wHSYpU=XH+Fi|At(+9Bl?}9=v%RZF zXY&pF(1rvVvBvK=ys^#YV?x?B%Qy9-E3m&v(mcnOo`%Hum$C_p2}Kx}=_a|;xPKjX z@_2luz%IaC(fZntC8K2MR0OD7)H~I9sSecCY7gi!!*Idyg_WC(m3gc3-3x9NVL>Ua z`p@cltKi*muwi23wMGowa4%VAylsTt^enYJXILR@4HuigPJJT5A89 zC>4b{f0R9{^=ZJnhNaD%ycv1eCi+Yr7@sYNm)1*=AlxCuc_BnBsYC{uEMDHCc%ej% z>aey&Kauz(Epx4htwaX=OU|{FwA3{zNK5}dL#(-GM{fSYLP?>h5LcIF6orZm<*-%U ztr%F%sKNZ&L96HugBsw!I;VAFduS)FwVW4R5e*2eAT( z8!DP^DdD_sj<`*;PlL57`I&~?qxra7cq!?h%$?PsTXl)Age3%2u`N=*So^RPClw1D zN2KS)Ax(K&Yc|y;BcV3B!Mj~3!D+Kc)5r474X6+<<^3(}sKCG)S$ctCaWnL`{T+M<}ytnGbJp2~_NnDrqL>fGb-Z?%hIa-3f z^=(z3u6U+GwSK2@WmT>Px07e5Ov|2{hxrNi;+>^Ch)R)vLVZ);WI((r&2l1VNhZ!Y zzIKGvFKvW}pfA2Dp~6b&D`UpH%&%(kM0Tq-vDLo;#VhaT|CJk3j>&wJC_#K)kDn|* zBp2nZE=0?+X{F^A59@K$%uaL49JZVYKf60VTmMdoJ%yQh2Mbp^@Nwb&M4h_U0ij)M zPZH{zP@7ti)7JQRC!r0}i`VM;h1k;aU~NI1T!yNIPxuH z=G2sFnHXItDCsQeD21|SW9^@{*>xD+I`Ge4fV}Jd}$ zhp1Ubq{?pkUFP#ibC%&j*+{`FK2wbOk}%mq`8ox9mH#P2RIO_4(OV3&O@~Zyw_Hi? zN$yL>l)UlFe|PLF#enjE%4sXTsxYp4QSE09mzwad{ps4|b$lK+@#+L|qDAr8DvwfM z(_GhJj()#+nB{^6m#j^~prUOKY%M`eZSB!oEN`09%9c!MhoAV0Kq=lT!(QW5izU;X zi}?kAHl7e|O2&#L$d;&yS7w6cO>vL#R1R#$`+1Tz->k>k_(SqB=1vQKOnM->Zj+L? zvZTebZOwZNL~B*n&v;2X|X>!jOb5LXt95 zBpGP_BL^?*+nb)P{aX%I=E>Bh!ZTJJE&8WO-}d!-W$h` z9j+Q&Ex%eUsi;kvmNh*q%nm{RmCE+&MGa_dc;4_`Yu!rt$}4$t{y-k8s^`?R+II@D zOx-F!q&TR?uZGu#ZsTueEV5m*xu<-Yiq#oo?OzmB7ZX9$!tyiaB~=Kjaa(0-C~AdU z$8uexeo7L`lCx9J6~8UUv6PD9uUEdRgvqj}Y*zcrRt!tg6-AXNHX?&(7YkI3Bs@$z zUommT=T$^fIY?qs@+{bDb;>TwzHP@(l|7Z`8gI8EueCY6RLBPp+G*^1WR3Q)J9jHR!k0d<_<6T zYgt+;E>$mU_^Hv}iXrW5L>G(33(ziZvz$%2kb#e~@0X0L-`tA$wHwxJv!i9`NSR!u zZof;QJbn%TE8!jyzE%y_?#-H&kISV~o4#s&L0)@X+j{+m&&Ys~eo!+seq@xkt+pkm z3`&DO^KIs%Ldt>Vj>Ri~Y)By(?E3Qj8po7oT&eZs?T-ag)!ydQZu&-;#dig%ocUwFUs9`XL*;U&+NpTH00L%~<^ z-p_`6!LBSbbccrY5W!ZRz3-&{h9v<|405cK5pKEZPVT!oa7CLAn;uh2(0R2U@04B>R) zEa6Nc<_Nh$k#GqqxfJS!uL^8JBnnf74q>Sf*Wzl0Rl<598imcm7GbLpp9(({ej@A= zVw-TgaG!9G5POBYg$IO3gg7ZYBfKHJC4_Xv1K}fKuMp3L?}ZGJy9hp_fub>@aUx6- zeI$w$@kN*?S}a;3Qi~uFC5o~|HWJw^Di#%q>O@#AY7%9LDn&%fy0!lE`W+(d6de{F z5Os@iUi6FTchMaY{uDiGza@Gq!gJ9pQJ=_33>R^P-9t=*#0QCe#Y4p-#h5G(6MrO* z5Q8h8FOC)a@vu<5RD4ya7Q-e^6%UaYijgZW6}O5vit(9vi+GoKH(|oWC&XUMuD9Y3 z@mur18UKoLU;Ie?T1-h0CE-dYNG3`!OEO6^O%g&<@FhG+y~HlTDv3stA<2~>UD6~e zlsF{Fk(5gsBrC`^PV$N5pk#{#J0#ypzL#tzAhqPU9DvyDR^;$xIZ$c#$j6A zl(>kvq&OJkCdVbmCB|VzoFlF}Zetu)$JNJeiQ5&2FXQ&d9gjO4hwiu&amV7mkHe2~ z7vnC+{St?Ni4}QEY z{#blvJaqBJ@m2B7@z@x@FaFc`ZSmM0e9S)^(@(4L*%P$p3S^lf>UnC+Rddxmbvy}+RBP0F zwNZ@)YP&i|U7*I{^~LIq>TWfTtG`!YQ2(UHRrMY9P4%y8+)&?E_o(ly@j(5L`my?{ z8olag>R0ObY7*n*s!?f_8n|mbHG?%F8cftgXreVAYp_Ja(@fR~HHg#1YvdZ81}T~> zO|GU)gHlbgrdm^}A+hHxHS0AU8nkORYPM>2YH(O{Rr5x3SA$oY$C{g(eOmNug0xz# zKf%ScA=-i3FfE2@eYNAYqqP{MouN(8Mr)C%&DCnOi?o=pU8*%`=W1cp#%PnZNm|IY zCE7aeUs{xDJGJH7HCnW4+q6yE&$X!0_Gk}kZ;{4C?S5^q=7<)jh}Z3!cBu}&Iw#!= z?Mp47d!~J@rRxx+d!v1<_0VCUZh>yLZmbStbc=LzbhC7rpqr|T(#7a7PnV_3)yZ^_ z>k@QHx@;ZNbS7PfPNRcLC)U|@9Xhn?R_b=>R_oBItJXE@w(GDHhw z=uYYIweGAgzxcckf9me&Ug%!y@J!dQd#8J=!+Ra{E_zZ@>Y*Q?_t6g{rI`94{W$$3 zJwo+U^dIS?_2B8%dV^k}hgNUZFVmOmQKet5Z_+pG(W?JhU#4HJ$1nPe`fv0<>Cvx0 zrT^LZy&k{m&*^{AAJ^lwevST&et-f0>L2UJ7-$C4*Z80Qxn5wvY{OE+a3kMLZT z!?4|e9K&Wqy`jp04Tb}TB!kU>Zw(I&#|)blq zOz<;~H4ZXbj7T%)7RjL(dCVtj3c>75ZnOv6n~(0n#P-^n!-((ZklF_FfF#@V^f?- zYFcW7%%nD{OqoRXX(~09nW}5B#9Hx( z0yi_={Kk}JLAZIEd8&E18GQ3R^Fnj98EUi2Y&2WUu$nbyy*b^CGIOPQmAT4{a&xZ5?E*vtqfGvi)FnvEiCE*f!eMW5od5dF#*C zzpS`ooo4g0F>Lt7ddJGK^;vPt`k!^IZ5+vGvyHR`*-lxJZ_BkQZFx2%*(A2*L>`Ao zTedCACbuEfrn9MSDjP~{iT|&?FAr<#=o&wPTm+PwsMq3xf=XXpuvaTBZAEVaQC!eS zjp9PZt%Zav1QND{Y=k60LLg)z5TI3Bi=t?y#ah}}wY9dk!K%dti?y`2)f%^o3-X)b z%dhY6=^x+o{J!sdp7)Q}W$u}|XU;w6%$b=xnLFoX9LU(40iKLcGmd0@mjRz-e3Nl) z!^sTj&N!TLG~?Y2n9Z%sXw0a~0BgpgNbkD|0LA%QQT)- z5G-ip!x#LY`R)AA`EZ?ong2Ea8Xr#ckMke$&-39Y{#pKS{6F{*Ef5H13&sgREZ8Ji zAebruo?xaxDo7H*WI?>(6~PJtOcM+U!UVAbPz#y`4#AfK_)zejphK`v0LKIe1ZM|(r|^&vJ{L9%e-NG(!gb*pVVCfh5dIKe z6b6Zgi{PPDiIl3$j;a!^-Tg`nB2IY)H=*W#?r}vY{}$Ap1o2 z``PeK_Tg-Ic5gQPk$pM)?HnuzggKEpec5w!;FTOs&cd8cIj}B=k+TsU5CKun)*Nn* zGzW@u%5v&+-pzrNIp5}-$+?vcJvmo%e#!YY2mZ*po#V~9l>>b__j9Nce+gic5t3&l zArinPqa~9h&q-jGWWFRpk|cqpk|mN2l0_0&FUgQ>le~!*-V$^;Mk19!i9{#Kk(5fn zAW=)IB~A&rBu$bXlFuaYzT~K+P0}HOcFD(*{gP7>I4LB!$V+e@Mfn5mJ~UohF?pC8Y4Gbg?v1x=0Eer76|5@<qe0VLtAYYMRnh&o0ck`Du zH0Q(K{P*(@<$sh9U*@09|0Vx=K3vJanjcsYQUIX^;|jtHCKrG*e@?-x1q%w`rGof^ zwFOHGU~|Eif{X%J1BeRp3*-g*0KNrA_0&js|;mAS=E(|Dqp)j-%#uZK~j46yNgcl3h zg$0GwLP#ncUbwuFRS1&8?S(moZx({6Fsm@LP*4c2LPudkp|KF^3Y~>zg;j-6QJ7j- zQ}|UO94Y*|@Y}+Jh46V{cj2#v$hYzs+0TW&g=sPX*)Z8?*_}eTU-+ynMD}MPOqKmZ zHbXX91~18G%Hm|pWe_i0En6ugWx$rD$TrEg$zY9ayDU>Ck%2~LmhF_a$lz_+ZrNM1 zT{3uIc0jgYwpRv+WF0cM?6?d*lXb~X%Pz^_N7<{jH)XeFa98%H?4c|Gd1sbSl;iSX zIk4o*IrC%+_zc6o!mRo*3sZuw90%koEZxGldT$BKfBU}{lB(Ws(1MKHN2 zsAzc6lpx$MF zqO)>&(cTJB6zPi$Mdd|MRb(!*6jc|2t;keVTU1wsV$i>$U@4X>AW5-8!B(tN07tP} zk)qh7fHXzAB17?}0`e4UMTtVA0KGz|C|6V~pkCopI2Ah-uupM7aY(UO0Us#Z6LXhgF@|JRf3Z|>3s-9OZLq9s(7O57fUPH%+RgaZfDuD{NtKLxMs5C0jt4dW$ zm0AUjDywRzYKICosVu7ZRIMs#Q+=XpS9Pest@=#$k?KnooKkhF&a1jqa6)xK^@FNM z1;3~ssvfDHsDPsOQ)B93Y6w<`sA=l4YM8E`sg6~@paxRCPMxAoRl|DqCiOP;7B!@+ zxoVzzwi>e3x#~Q1ks6HZD)nx4gBlL2_o%O^ud3k(^=b88^&K@lR9iIunn!AQpuVgQ z&|FeOkmkHPMl(+XV>NR%b2Ov|Ue>TREY0&8n5lVI6ROc_z^Q50G-%$|fK4-1W76m} zP^yt>+BDf3kZ2k;2F)%F+|`UO{$2Bp2KqJr#a)`=#c)J(Uo)(@M+3JtA;nXQ!;7Iq z6I&dheWU??@r>g6#p|_DT%23XD{d(UL9x0xqj*s<%qkWZ=NHEm!^z?;#fOSNEry2T z^Tnr%4;RCQVo&jr;?IlWT=84Q5!!)bxLf?JHdH%H3zN0Dc8vCMF_7A=+7xY~78qK; zl4V+s7L?kZTD`VX3#Hm!TE4bg3$@zcv^s5_7IL*kTC?^`EqtW?R(nY6(ZX46x3*V% zNed6O!6l&WhC~n@Zj+fvgfs$&QluN}#!55a`zFUej&W0au6V-_|wg zK&{)S`%2fL1GmnjJ7?RigJZh)bzkT{*TG@kP2Fu>j}AuaL-bE{f9l}A?iu}Q9bFG& z^|*eDK0*(3^e^gP*8c--!0QwAOntH**6Cl@Z_)GgpwbuV>-7>n$n|Y{v0kkQyS_@l zS6`!txAjK-PJOE$KGpBnAJrez!x?>-{=B|h4-fQyhR6E5dbp;)t*07B7$Dd%+7NG; zWPnJ+48u$V!vG0}R}C*3Vhym|5N1d-yl#Lu4BHHR1J?jTLyjTWP-Fm&L2oEESPbAW zyk&UL@QwlY81@+s7(OyUhv8F$+i<}Emkiy8UkrB*&}Z;39a~B(g%PC_O2bMcOX0;* zM(Kvq6TJ$sk#)3OLvvNRa#jJO{MRYI!kwy!m-j*rJt2vD23lk z@09*h`co;~DjieSQ~F~m+%LUcda-mw84N2MUv{MwImV@xJzExDhI}3Gwm(-kvur^b zM3phhmY1z8gB4{<%GQ)^D1-H7ys}MYnPng>%PEtWDa)X|%v4rYW+?+(SxuRQ}IZ%z5G1xfF2qTRlMw)R&1B^DFDj#DUYlJtA zd}Ed|+XyOSjqx+%Cr0?l_^t7R@f`Xx+j!CVmGL(t{BHcgc-8pW2!W;`(||F|1d~iO z(_|CV1TiMUG|#lu1dB~eOv_Dd6C|3}nYNkIOfbW=)x#EQukFri{Z#d8%epfA8GUanwOFe_j|#qx?J6^klhMa9~RjTKwams=IvD(Wg4 zDqu%NOU1s54=dn8MQ_EA6~9%$%?h3QZUttB2Ne%1=w=+9QZk2_Cz;2ZVYvAp<`>N| zW_a1$SFzH}Hp41&viTYF5;H6?uQ8{XUpGU#S!kA;ciBK}R-5H!l^K-gQnSHaVTN7i zedhhhxU<)RtS1lVYkY52hUkzibM_2n-532@R^^4Urt7EDmqWYC;Ms;#EBvfyxPOIKh4X;;otEX3qszFkn zTP?3%Uk$SAqH0yOq8ijzowd|jZ3UavVXd(?T49&L?)cU5 zy94}df@((9jH!XpnuwaPnn^Vn0S1FRZ$&9a(RHIQ7hyoOU_ zuK`C*Q%y_F-WvG0=7*Y#HQhDPRnu8>qvnqqxK(qz=21;w4N#mTotX2-8W`uC>3qq_ zastyyI^&%SP}Brxl5>M|trIpmw>Y;uw>crh$#Zg@Ely~4e(ZERzjZ>V^E>Am=Vd4S z;QY~f)%mj%ZaRN={^7jqgj>%0&PUFGS_rKjUpu*WN-a#Sjja7gZB#8xubojlyY~57 zm|L4r8(+H;eI{7DqIOknN-gBnT52`5m9=22wbwS(HlhO&DbUP~GUd5p@A|5Lh>=F1U_b2ea!Y)J4`! ztAmI-qHb~B+&WlPx1er)U0NNyStqT_tNW!6uGe|%f?R&CuBooc zE=X|Uu2C+A3l_Q-xE8q*U6AT}-L=hyLdddR>s(@&!38?kZkNzi-~y*>r|Uh}0T&!| z9dR9YwY%UW*XOQpU7xw&yz4txpX+BA+;H7=^}EK`!-V=t^^*GhdXU$v>h<+y^-x}4 zRd21YtOr+pQ~i7O?e)-H-%`KX{&hWkQ~zzfr~XVmT&llZ-(BBR554u*>TlNHt%pDB z@7F)5e^d{T>-+16H3T-mhf=&DqG478ENEETkle7k0k$@v!v_tU8{mzGHybh=xDAlo zAa0O0SR0_Op|RollrJ)1oq1c%rByXqFrC)KepZ0BwYB?O7TtTW@K^tp5SuhNjChck z3+narKh0L4FFL-jKD=x3d$AIjuqY-aoV4igB<8xHz4tVY!Z$v!!DSKYZ zKgx&)XzbFiohL=Wi4|}1kL*T3TxHr%DOci2C~wOP+lMEx;pUI$ej2|lp$g6>1$^sO zCj1FYuYBPb-Ij0=T<7!S^L|V4(3Imqs0bl`SQyhgV&4?#S!j)EtVUt-zFjDIBZSBx zeJ6PRV6%Wmm}77+M6CVh(ZE3PS1|z;qw-@zmp%Bv>ALwZyu0fx3L&=S<+yk6CP4ql zrnxSA`L_`G(T2!);U08IARwI{kOq{IY35-l^1Mj9&o7h~!0y7D7GkBF{HX!#CBl>9 zTS4YbFExCaUtr+KVS#}G!vcq4fII@BKNNH*xv?Ch&7@I%P8cS$sey1ffJQ}rXQ)*F zKq{gd7)TBDAL&&F4xLUO=?yab6HidcSwt(1;y033895! zDRkOq_xG7v8ojZ13_pPGu9i>!gznxINsn}YOs6HeQK+PeboWo4&(hs}`^QDPFOHzo zBHiJ1*cs`bw}wu8*_}uS2i=`Cs+^bP7SVgG!{{FEL^{#7qkj(_`4?#28r(vqyU!HR zA)W5NMu&}b_hZgf3Y!jr(JC4p5;#9bQ~IlL*oD!X-17xEG~Vy;pWBbVoiJxHAiZB1 z-w!ul3v*H=1?=U?K+T&3!)lFhJ_(vIVloXTo@X8m1KW7&OY?&Gmi`y$aE4CfsJ|9O zj>6sBX+Pm`V!Ats_Wk30iknU|7*Lp?kJpa_+GY)%Ju8xEX(IV_*mJ+1|L*EYB4Si$ z#BHrc774BQ`#Xk}ed%6grLL91_T-(LQJud2fsKJQKL$rn#E|Y76dfGgYuZ%F7@3HX zOK&q4b1rwkx4o}~dg(#`n1O1QB`GZ>Ng1~07I7V&0H6P`|G~UWM@x)>>WBRU zff!{@HgnGC_;omMowk`md5e}0nUDGhE)8=7H8-E|W6~B?PY<(9O0Z3^PK0>JIQw{} z^I50~jjzLN$1a2K&YV5>eb@O5aPblopQH4%ObV4u#X4FxSCMsv$bQP4tIsu zPxezn@R{!z=gu85wzqO7UIK?%nF zb{75M96+&I_Ah&j()8V{e$Rxn8GFc3xI+8wn!#^p1f-1XOdcQ6JT831b{cH=i$0L` zT$IF!8b>s)H99;Y8qzmJL!-7eIe;xo!x%Y|Xy~Fm8K~xjPmekB8b&Tb-b@l<36VPb zw=k;TlYwgnWjh_-8bgO&2IbetMP&puZ;NSVrzZDDG*Pa<3?C648g#5%PklTPynIz_ zHD$RZEo<7WEoYDeS;l*+1Uh*9qazQbprh~pky9dX@2B?$feby>|H*(7i#)wFYb*Qt z)#bskhqIa*WrX=m|JgJqjVYN7u!`NZM@L zy$IThuJ4(bE;1;WyU$-(Mn02fp#@h5Sp#juYJlqWca5kYz6k6A4y@L%jsi=&zGqyz zxSc+qltUNQwdzRcitU@3>xRc~f9r7yH86tx;pzx?FBzUdosYtCrP6jc_^&!}FaF~& z5QV{>gd>e%%lc8zLfz8)`YfoX!w9;Pio(Kikn8VN%1}BKqt3_aP|cbSdD9_NqFl_e z1iZXCK9NQzQzFTOH0Tv&(g7)S%<5jHG6Fu1fT*DTi#S26uu(ga(PK{rhM$QcNLOI} zuy{X9KsCm+Qf=VRS*J`{K^lllY5oV(A|Tyhj-YWy&0(*<&fK{Qb}gW>QY*t~XOlM2 z&;3j+!PyDV%pks|S^aF3Ps3v=pPf4jT4pkGhto%gb;*~*kB^Cf>j6&&G6U`@<1N$` zoooZGd~*cE#)QXsr%&PFbeJzkOim+%XvcyhiD^lzT3M@TC~P?`=OK`((;@LVeJzEb zmcXYqEk;4Eh=}me2-p|_dx;1o)8dyng%&pLvxmq>UwQ--M-btznbdm!1dCtFbI2=t z6fLT4^mO=o*pu-C4vr_@>bE+Wdb#^7o5mOpG!)IIoDQ+b8Q`ihy`$T3n1#bUoVMf{ z8t(F|r-&!ECs3BN1D^~mA5CLCL(p6S^%&Datp@*R(hF_cj1M8M_+ zPNAWX>g0hG+Bk9?g|4Tx`j4aZ1X9lgQIwP*>Q(kYZ5U+?m7va}Y@8nmZjv$^j!|Z{ zQjQVoAa;Iy*r>g{zdkDDiMT{p=$!kML4^27`r+9HMWt=C106I>q#n@DmPeyx=Cu>jRBu#<49sb)nH;%Ga zb$Gn_5`yUNj7*Hh-THfs77}+WUCcz>otH{Bv#p({ILA9NMp~06iE%j{cS9sg#PS?x zGIRh}W}Zp3$=>j;X}meW#duBsu4XPnfP4h68se zawKG9Dw2K|?q+w8Ev<-Yt&wP#cv={z-MD*K9PThuZ}R+UT$yz1Y$TD2sKz2P znhwH_m^Vi=6D6J-BpYe_LUV!IA2dg7!|*9LLfdUe-L+U{;?LQEQt>B*op$7}0L@H1)}mdAS1%usJ7|vOIju zCq}pwBdkb{zT3SkkUeafixJn7B|Sc^vXP%$qAxkx{fChN-2L9J9hc@IM|T}7Xh~NN zVnk?6C)1BZ7(Kl07!k$t#H~mR>kJ{&`G_bMht%y2A@kNDngMalXio@<_&LO$9>$b- z2tE_{Ow9}-U+eJjnJ9@QU*mUpS`rW#LZY_qUVZqr4tFG<#5!N{;a=z7(cSv%R=Yqt?g(sTKV|gUx zStZgQPC5`@-K(0B91>p}nWM>8)RkdsOX5>)pe7`veSJ2(gX}?f*xjp8+wz&-HPIbM zaWd4{qa}?!-3(*Tqev1bx^J3#i%j%@KL>_L?bc^)Rz>utO-WMrU!{QWj4MT z>1XhXo=G#i(Ll#$wZ)@86!(Ihm4ZSMZA6_hdZWCv+Xp$c%c2l^G`X9I_MDcpSV*0S z<&S7xF{lG~@=2fg*VBE}k*<|U*K1Hq(Qrc90zS%YASLwm9wh5gU&D(s$nH7zv-=;@g;Kds3J5c6gdd@@$V}2_lb?hmoc9 zMf;eg-9f`^IvTyTOp&CA&pL}nv1sJNud@yHv{(s?G^cvDNR~wRNEYGldu+t)mhR4b z2}s3aBk4wLVdM$az?^7b+rkrkaE$i$Q#ho&(}=3H<|Q$_TdbWU$PSN-8JO>DCmNz` zBt!9@-KUt_ubg7aZ{c&~SK62@odXz7Ak`wPN<`X7^GD2kZd3RKF8W&Dh`8;FCevfC zxR{8`Yi%)ekY-ab@;yE7P9K~k37l+pNIXW=;NB1d%^*@=dm{}a^|S^=vLKpX`%5!ET%(7cVbX3nRv4m~CpCMgk z@bK2OZF1iT5jS_R-C8sJtcccecE{j6*Vm2{)m^w3mHprUPpoq?@tzUHP)YwF;Erwe zk0bD&FZz*{=#kGuyvGOq?!b_|gWxWV{OH6}upL7~O3z*&!U+tC`V_Sb3ChRO{Mn1% zUTz0IE$!nYte$UtsDT)o`=6qQBF~*q!6-yE@hKRKp@H`))kKU;|0ik+M&>;Q(-Erv zDVT?$k?$$0{{Nd-!QXfl{M+&>{2Q;re_LKff8$m3Z_8`e-+0aXx8+s*H(tg6w!CKl zjo0k|MP7aDgQw$w5BlerqCo|f$67QM`qWC-ccp@x%%)Mvw= z&&(LL<@wBQv$KV>VNL*zGLA+i2R#eIL*(3V42tiIeyI&!yn+MG9;FxrC*5(v=kg+8 z_oP=}zTcXC8qnDV!B&}pD)9>8DQaO6r;^QPHMH@}DTFG6<$-31$F@o65B5#nb`Ew(Q&~??DlNyyb z*oo^R32!&q)f7Yid1Fyi7~4n^nmEGz8b(+`lgJY-r2bwik+}3`2+`FaLxhHrX~{uo z!HQOLjxdsz{7y_#W15zXiXrc=-i%o8SXsa!l>8YO5iXSryH`fGvg1}B^8OJ`?AkGZ zuJ`q67|C8vG7A@Yc=HLXSBBk%)!w$dA!K+C*1-%Rl>yNtweG+>Pb9BI-<}>x5=lKt z-g8ObByZ$)5_!K)BHcXn&iu~7RV)&zC8_672stB;@q!O6$$P_l?>UUz$TBCP1tv*b zb5M>lrgoV3Bj5qJ`XvVv!v&K5A)U>@p4`V~vlRGO4GRoEc5d-jF2nQA_g1 zkkgKn^EUYq37l+o#KcRN9Y>gKoFKdy8z;Sm7#SWz(!%}1V{V7Bdt*pXBs&Hz28ig} z{p=etq&b8=HG~ndvD_C{Kb_e0pHBN2bJu9ii$ zoi8HhF|rk@BZS;5qNS2lJUq^ebe)4C?pqnw1d`JGz2(M1J}W_pk%I073~5*vIq&(Q z&4s%|KlTh=2Zn&5>;Gth*PAu4#~i9?U{_&8S{TXJ`V@hYpa~(>aY(MyN~9e#lD>jP zR+(DjjUhz%mKO=n((>;37!rwwR`Fx5s5azsxd-Y7Ip(1A=KOLTbloH~`*mE}AT=m*!!8WllV;g1mo05##OBRb^vnV}dD^v0Yys=%3oXT&L zz}D?zoOwTnaK}eA?GH>!YiT9o$i5J=g?0!19@6WM>UGDiZsTkYX=Jzgw={|twfV<2 zD&xqm5E52DLA!nZ6m4UxgnT<)>qa&ojs{Nu8QNB@*Ggg-f$S-5t$z}E`_3G52-(#( z!=K-Ht&LBlXx-__s3)KejA0ntFXWXbWDn?H07jry0Y)JGp=~g{k;X!!p=_ijDD1Q}cK3mB0S&~KC($6MJQ7*W;Nj-e?KEzUd`J9^IJ zR)3Cir6?fGJ;uw)T)ikdc9>|gH!OWztk=)gg^~A2jLr8Ymj~0KdoUeJ5b6+!puq}%4J*1PxOCD}V7X5J7)KMV9NMnl(suFda$|>JW&aL8^b{Fa8B0=x?Z5 zPpOIrsSc(?2|^tL5j05kul&WoKm`2_Rs57{_8`^4bSOcnLm+|%ss5F}_!o$vzoBM7 zrJ6HHbub-D5b6+!ARkp^CTRM=*ba>R5Je^Mf$hHJ1TrKHZI7em8baL=!pOl0j2-AJ z2pzl^MqZZaDVz&{weg|>6}Bf{wo^E-T}W5H-xw;G|B`eZ9N(U zF?Jy}xY0+TcZkZ+^}nJvIM@v#KfV5^%765MOlj!vf6QS>6Hl-Id;0Kwh~xW=2PO~r OKLfsi3ot7V;Qs(Gc9`V= literal 0 HcmV?d00001 diff --git a/data/sm.kip b/data/sm.kip new file mode 100644 index 0000000000000000000000000000000000000000..a61f679b85cea502bd2533ba6cea87382e33a264 GIT binary patch literal 102512 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�^miKnqnnPX#k~K>rU@Qi|B+3j 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 literal 0 HcmV?d00001 diff --git a/data/splash.bin b/data/splash.bin new file mode 100644 index 0000000000000000000000000000000000000000..c85b32f9ef12a0fb7bd064d4ebad9a973b31c47a GIT binary patch literal 3686400 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%0Djg3Q+_aUQ6?s@i5K_VLM~e8fqFZ_6GrvI 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

8`XVSn8Nj9y{R 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 #LcSAtb&?H4 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%0Aw2%d>b%e?s4jM)Gu5e!o#OsO>H+W^)qcPB zaomUQf7p42IyvqCS-(N{tbe>^Q%^a68aK$E#0|8&^aIjbwzqiY{%FpY8tqD+dsUbR zRP_Sn1xjEJSXn?b!0L+|H?~x906aUw^=>Bs?g1?QFio{~gi)yV|HvzqZW@iB{ob z{ivuYb}mQRm@#8)$dDm6cP<(KJXV^`Ec}^s-rof<{QE1%EO$$W-JLhX?x)PKthBk7 zwPb-krXDSsZx5C(u&lMq?HT#-&kt_57e{wlF4#No95bY+4_Y3Yb1}T33=Ce1K3{Yg zYzq$vJV80^1P}+1wr?)hmpKE`zWi`9;80=m7@TwJ}$45Ll_;WlwUrS#g z+#ArjaKHRK()G{R!1`RDf)iDEUmhkmdkf|l?ss~>!~WoZT`MZ0{mbvezu)yi;6j@D zbzUgV?VdYoh1bqo@$G9)+b?hD==TjB zW6ALLUYh_lFA(urMvdMvRGQ|Nl-75X=NS?f*CMKYv47)>3?u*Wv&FI5<=HPIXtH?h5?D zEAZWS-`T~B|w)^8xMNf={;hfJ`9WbppbsnirpNSb7W z632q^hdX`0H~j(W`%&Gz9)b3IbzmN~gFAB0e1N<^@&a{=>SI53!2{H`3mNruZJaWK z*z*-#|2yl>3_t=qZ8s;5u}iI6+i5WVd9c3nIxlr0w=+`Fb_R}c6z%>9eE`)VReM!O zuzyE8#_fw;x;dPmKWK>M4otKh@=ab0@T>n|yU;V%#*@{Nh=*U=u(EbWgT2QD{u)l}@r=b4}|H}h3yG2`@*Q&jxw$%=Umb_0x`e-@scU^ly z!vASkNLCge5dIE4J$uFqva&gUXYbMeA1|6|H|9*Y>^(c}^}~BsSwi2S;3=6p5Af38 z1tYUZ>&_*6i`M`0$|18Rxe>M?*hBIVxiqzPrqQhhgXZ?3B}U7 z(YD<@06S>&$pE^QI?K+EOm_Z%r}v8k;1P1?zW(pb-u!&p^$vnep&(;a z*xpsp8&E$0jopkDCd$Uq$~9+`l-((iW}ub$?&`cly81 z*SOw5V$uZbOP`=aRGj_Dyl&GD^7DJWfH3~o41h3xi%x+DDA`)m18CcAxZ?r3zj?<& zu1BCuQRfMw58$#v+jv=^KQ!-QU(|0v4*<-M_op5^!RmeP;eVaGS%4ZG;Nu=3`GEXc zfbY=%DVGH#FHo)c|HD3jW`bQV5VO4U0R{el=j{o!|5`AAm#jiE z75Fc|0$+Xg6k)@?HhhuR${JR zmXhn^S81`mE@7^>2pnE0uMg$>emwt=us@nT*gq)81-Q@i?=TwxP7h{y1I^y!0ciK~ z@WTV1`J_ncIN0YxphUbkdKil<-*X$Z~gI!*Ce!hc$=OJ05&s~P7zOJ}{*DLV) z4vrJxJyOPyvVzoSc;+%gy*_};4TS?dig17|*9VAj0Qvyp0JkF?K)nEu16;kVetC8F>Ds_iOLh67>893szamoODZ>y~L(YoMZ9idGu)C#X6%CcL3wIYt+Kp^I6sr zzjze>8+mbhq4CF{2daAY9_TQF?O3x91VU{~u(2cF}88a0_Ms=qMf#)uEfE&^L(Zvos1%Mo&K5HBUR7 z_gXqS#V(=kUjX9^^IsvON12_<>rq|d{2aNRCp$*jaaUdJSVzC``yczM3)~?JpMRVc zCnQ@@!cZ$5oMid&Lo81P7s!PR?CILmlF7_TB4cN$@ISa;UVE=N*KIsI;ap1-7VQy>3}19&|EVfr!Pf0Zc9l|F^NZ^Z*v{GaZ7zWp@g*t^V(LqehLY{hb*< ze!P3E^u-H%aP%1G6;=vHeYkj*WgppZ@5;#q^Iz*`f=l5Md5`hYKSV?S#VQ{DZ0~-# zjQ5AR0Q^61;RD(S{0i(WJpBB?cDuh}6?=IXJ05Urq~7pDpB$%H;IR zd3oR#yr0V>68~_Rp9~~8Nq!jf3-|NA2AJRBeQErGuOIJ@^!_|>zVbNp$>u3wPPg#t zIV%FgmoTSW`sluuK6&iC`>&rpv5MSh@G>eN?glq|dFP5fK7Gv2ZQWw)=EC=x-5nU4 z#P4>3weJ+q?5?o(U~3;d{FvJ1?^{to|#IY6~JfZBQh z9tUvq!2V2dI2*Ku^8$su{^9^N@P8|Ahl^jpI{aVluTb5Mx+_q31^!Q6fnmdjRei?G z52Ri|H6KrH`hOVzE8`RV|FYQLmK4~V;(U9B{-i1|Dzx(ALa;yh9}ZB?@l{DF**?nm zp}^hB`jIxTnScQM>lnuW^79Lm*Tnx`wm;ZkINkT`Yd#?Rv2glxboyuZ0Dq41doJO} zKZ)n&@SY>KZ`*#`yLF!(*uLM6aQoDe({}mnWxIXt7SDgcYoEd=a;zX9ZxUZODDe)> z2xymZc9x$N#EybT_X01_Bl-aH0%?!1W&*1E06$;*g&qL&0he#HQ&{-_CVPc%*oE`g z@dc?Dz`VfeD{z3zcJjnUc)$hrIMWj#Q}m$k6WYVHqlb69x^nc>Fj6@ZF+`4d@7N zufF5U$NkmtzsCbQfd6~&{3+yy_5k-sqxtpXvpNYs%~WOxQpu*gispZ7Po15QyVJ9nj`>?EAwWT&onqel$bf2frwjkdBOBdjEG zn8yPg7eIT417whylZYOe1olrB{s(K1`=C4?Fvjl%s0T0C3VCr;XZw1E7h z`|atDb@nR%dE_zm16;jAPtFJXzd-*7`&VY&^8Ehbe&vG7^B?4ZzPx&xIbe#p;aAMY zzJv=Dfys0BZLufl{rA?T+l}R^c75r5`(-tAzxdgU=o_43C&2xsDV8Un%NF5(eE-t^ zLI8HP=I$vxi(ckAo>7q7N7pqVK~7tJVAJ-3NM~;nLN(NbMKawGNW6HzF+?Ik-d4w4mSMy+QsI623fbQ z?8ZfVc;c{~+qT6vFIsMM@bDzXqwn`hhI6C4Gk@DMYN)mCImtTrn`_+%FSecui>+(F zS=PS$Xlvh<-`}`on>b+(nm@X~H~&1}#J2k`pexT<|KQgr$5ZnF;hA0M(@_sV z9v#i<-u1NkySE-XJ^niS`vvs&-v=&ZByn}+p%$% z?Smg2-G9u^96Mu|&s}CG@J;#(_j&IpZVp)-!Eq1YE0hiQ$Relc;XV0*s^$T1sSlwH zQQ?0)KyII~W&-2`68?8yARqsq68={o;3D|{f*pnf9D)NJI3RD(Df$5?ZO`5lwtIKL z1&-L(ZHM3k2jK(z=nb&ji!4x=_bI=>n*s2$KE(%Ca@)=KW{}-UhQDThl>szAb&bti zkYRJ@ucB7l+y$#G#jm-knKp0XT1(+Ma~4pk>um1A^)`>3p49ZMmb!F{&6=CRY%sV0 zE};A$;r_170tXpD9uN39{%=p8AeKJEFnR%p#9}WH=0lptWuRymo6QCL+y`Mb-$hMDh|P zy7G8{;{agPiS!^Qi%;-g@t3V+U5*6>>4m|_+C!L)Ps?wX}` zb>VC~Gh>3?NK3J^jP@-(>$dwkfX8~A$A`Gx)SwHxgOa{zeui>{o-$8*JH@|2Rv^Ac_U&C`cg@eH1o z$8|5Hav#A_(EYEUw|n$tPHo<5>(W-*tVyYsG-L)dIODBzuhHn~qpeN%an`oi6zdQ> z1H3=SqWaIZC>`Twxr*j?&-jJbbMQiL&j5Q5x3--Jx%{pvljo!T%m1SufR4iZ9_APB z5AIvY{qvX+9y)9ae!wKxBW%*53p2s!|Kb4X_cb{{Gj;$d3sl%Y!2fOO0d(j+!8*rI zb9^A^19-gz%>w�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

E)|Igrc!pXRfkz@6Jl(jFuKWTzOdHwy+Fa6j* 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 z00RC9kpKN@Yif!B0tg_000IagfB^ZQK7arM2q1s}0tg_0fIkA{e}CGVnj(Mz0tg_0 z00IagK>nu>Abqiqt*I#j2q1s}0tg_000QKH`Tzn5AbrHLfB*srAb2sF{`aS?sVM>oAb&_e0tg_000IagfB*vIfBFCd2q1s}0tg_000RC9kpKN@Yif!B0tg_0 z00IagfB^ZQK7arM2q1s}0tg_0fIkA{e}CGVnj(Mz0tg_000IagK>nu>Abqiqt*I#j2q1s} z0tg_000QKH`Tzn5AbrHLfB*srAb2sF{`aS?sVM>oAb&_e0tg_0 z00IagfB*vIfBFCd2q1s}0tg_000RC9kpKN@Yif!B0tg_000IagfB^ZQK7arM2q1s} z0tg_0fIkA{e}CGVnj(Mz0tg_000IagK>nu>Abqiqt*I#j2q1s}0tg_000QKH`Tzn5AbrHLfB*sr zAb2sF{`aS?sVM>oAb&_e0tg_000IagfB*vIfBFCd2q1s} z0tg_000RC9kpKN@Yif!B0tg_000IagfB^ZQK7arM2q1s}0tg_0fIkA{e}CGVnj(Mz z0tg_000IagK>nu>Abqiqt*I#j2q1s}0tg_000QKH`Tzn5AbrHLfB*srAb2sF{`aS?sVM>o zAb&_e0tg_000IagfB*vIfBFCd2q1s}0tg_000RC9kpKN@Yif!B z0tg_000IagfB^ZQK7arM2q1s}0tg_0fIkA{e}CGVnj(Mz0tg_000IagK>nu>Abqiqt*I#j z2q1s}0tg_000QKH`Tzn5AbrHLfB*srAb2sF{`aS?sVM>oAb&_e z0tg_000IagfB*vIfBFCd2q1s}0tg_000RC9kpKN@Yif!B0tg_000IagfB^ZQK7arM z2q1s}0tg_0fIkA{e}CGVnj(Mz0tg_000IagK>nu>Abqiqt*I#j2q1s}0tg_000QKH`Tzn5 zAbrHL zfB*srAb2sF{`aS?sVM>oAb. +*/ + +#include "hwinit.h" +#include "fuse.h" +#include "error.h" +#include "bootloader.h" + +void check_sku() { + if (FUSE(FUSE_SKU) != 0x83) + panic(); +} + +void check_config_fuses() { + u32 config = get_unknown_config(); + u32 unitType = get_unit_type(); + u32 bromVer = FUSE(FUSE_SOC_SPEEDO_1); + + if (config == 3 || unitType == 2 || bromVer < 0x1F) + panic(); +} + +void mbist_workaround() { + CLOCK(0x410) = (CLOCK(0x410) | 0x8000) & 0xFFFFBFFF; + CLOCK(0xD0) |= 0x40800000u; + CLOCK(0x2AC) = 0x40; + CLOCK(0x294) = 0x40000; + CLOCK(0x304) = 0x18000000; + sleep(2); + + I2S(0x0A0) |= 0x400; + I2S(0x088) &= 0xFFFFFFFE; + I2S(0x1A0) |= 0x400; + I2S(0x188) &= 0xFFFFFFFE; + I2S(0x2A0) |= 0x400; + I2S(0x288) &= 0xFFFFFFFE; + I2S(0x3A0) |= 0x400; + I2S(0x388) &= 0xFFFFFFFE; + I2S(0x4A0) |= 0x400; + I2S(0x488) &= 0xFFFFFFFE; + DISPLAY_A(0xCF8) |= 4; + VIC(0x8C) = 0xFFFFFFFF; + sleep(2); + + CLOCK(0x2A8) = 0x40; + CLOCK(0x300) = 0x18000000; + CLOCK(0x290) = 0x40000; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_H) = 0xC0; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) = 0x80000130; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) = 0x1F00200; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) = 0x80400808; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_W) = 0x402000FC; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X) = 0x23000780; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) = 0x300; + CLOCK(0xF8) = 0; + CLOCK(0xFC) = 0; + CLOCK(0x3A0) = 0; + CLOCK(0x3A4) = 0; + CLOCK(0x554) = 0; + CLOCK(0xD0) &= 0x1F7FFFFF; + CLOCK(0x410) &= 0xFFFF3FFF; + CLOCK(0x148) = CLOCK(0x148) & 0x1FFFFFFF | 0x80000000; + CLOCK(0x180) = CLOCK(0x180) & 0x1FFFFFFF | 0x80000000; + CLOCK(0x6A0) = CLOCK(0x6A0) & 0x1FFFFFFF | 0x80000000; +} + +void config_pmc_scratch() { + PMC(APBDEV_PMC_SCRATCH20) &= 0xFFF3FFFF; + PMC(APBDEV_PMC_SCRATCH190) &= 0xFFFFFFFE; + PMC(APBDEV_PMC_SECURE_SCRATCH21) |= 0x10; +} + +void config_oscillators() { + CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) = CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) & 0xFFFFFFF3 | 4; + SYSCTR0(SYSCTR0_CNTFID0) = 19200000; + TMR(0x14) = 0x45F; + CLOCK(CLK_RST_CONTROLLER_OSC_CTRL) = 0x50000071; + PMC(APBDEV_PMC_OSC_EDPD_OVER) = PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFFFFF81 | 0xE; + PMC(APBDEV_PMC_OSC_EDPD_OVER) = PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFBFFFFF | 0x400000; + PMC(APBDEV_PMC_CNTRL2) = PMC(APBDEV_PMC_CNTRL2) & 0xFFFFEFFF | 0x1000; + PMC(APBDEV_PMC_SCRATCH188) = PMC(APBDEV_PMC_SCRATCH188) & 0xFCFFFFFF | 0x2000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 0x10; + CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) &= 0xBFFFFFFF; + PMC(APBDEV_PMC_TSC_MULT) = PMC(APBDEV_PMC_TSC_MULT) & 0xFFFF0000 | 0x249F; //0x249F = 19200000 * (16 / 32.768 kHz) + CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = 0x20004444; + CLOCK(CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER) = 0x80000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 2; +} + +void config_gpios() { + PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0; + PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0; + + PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = 0x40; + PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = 0x40; + + gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_G, GPIO_PIN_0, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_D, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_E, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_H, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + + pinmux_config_i2c(I2C_1); + pinmux_config_i2c(I2C_5); + pinmux_config_uart(UART_A); + + // Configure volume up/down as inputs. + gpio_config(GPIO_PORT_X, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_X, GPIO_PIN_7, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_X, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_X, GPIO_PIN_7, GPIO_OUTPUT_DISABLE); +} + +void setup() { + config_oscillators(); + APB_MISC(0x40) = 0; + config_gpios(); + + if (get_unit_type() == 0) { + // TODO: devunit sub_40018D90 + } + + clock_enable_cl_dvfs(); + clock_enable_i2c(I2C_1); + clock_enable_i2c(I2C_5); + + static const clock_t clock_unk1 = { 0x358, 0x360, 0x42C, 0x1F, 0, 0 }; + static const clock_t clock_unk2 = { 0x358, 0x360, 0, 0x1E, 0, 0 }; + clock_enable(&clock_unk1); + clock_enable(&clock_unk2); + + i2c_init(I2C_1); + i2c_init(I2C_5); + + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_CNFGBBC, 0x40); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_ONOFFCNFG1, 0x78); + + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_CFG0, 0x38); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_CFG1, 0x3A); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_CFG2, 0x38); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_LDO4, 0xF); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_LDO8, 0xC7); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_SD0, 0x4F); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_SD1, 0x29); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_FPS_SD3, 0x1B); + + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_SD0, 42); //42 = (1125000 - 600000) / 12500 -> 1.125V + + config_pmc_scratch(); + + CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) & 0xFFFF8888 | 0x3333; + + mc_config_carveout(); + + sdram_init(); + + sdram_lp0_save_params(sdram_get_params()); +} + +void bootloader() { + mbist_workaround(); + clock_enable_se(); + + // This makes fuse registers visible + clock_enable_fuse(0x01); + + check_sku(); + + // Check configuration fuses + check_config_fuses(); + + // Disables fuse programming until next reboot + FUSE(FUSE_PRIVATEKEYDISABLE) = 0x10; + + // Setup memory controllers + mc_enable(); + + // Pre-Firmware setup + setup(); +} \ No newline at end of file diff --git a/src/bootloader.h b/src/bootloader.h new file mode 100644 index 0000000..7aafd4e --- /dev/null +++ b/src/bootloader.h @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#pragma once + +void bootloader(); \ No newline at end of file diff --git a/src/bootrom.c b/src/bootrom.c new file mode 100644 index 0000000..7f6b760 --- /dev/null +++ b/src/bootrom.c @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "hwinit.h" +#include "bootrom.h" + +void bootrom(void) { + // Bootrom part we skipped. + u32 sbk[4] = { FUSE(0x1A4), FUSE(0x1A8), FUSE(0x1AC), FUSE(0x1B0) }; + se_aes_key_set(14, sbk, 0x10); + + // Lock SBK from being read. + SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 14 * 4) = 0x7E; + + // This memset needs to happen here, else TZRAM will behave weirdly later on. + memset((void *)0x7C010000, 0, 0x10000); + PMC(APBDEV_PMC_CRYPTO_OP) = 0; + SE(SE_INT_STATUS_REG_OFFSET) = 0x1F; + + // Lock SSK (although it's not set and unused anyways). + SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 15 * 4) = 0x7E; + + // Clear the boot reason to avoid problems later + PMC(APBDEV_PMC_SCRATCH200) = 0x0; + PMC(APBDEV_PMC_RST_STATUS_0) = 0x0; + PMC(APBDEV_PMC_SCRATCH49_0) = 0x0; +} \ No newline at end of file diff --git a/src/bootrom.h b/src/bootrom.h new file mode 100644 index 0000000..64eb7ab --- /dev/null +++ b/src/bootrom.h @@ -0,0 +1,19 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#pragma once + +void bootrom(void); \ No newline at end of file diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..6debbe8 --- /dev/null +++ b/src/error.c @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "hwinit.h" +#include "error.h" + +void panic() { + // Set panic code. + PMC(APBDEV_PMC_SCRATCH200) = 0x21; + + //PMC(APBDEV_PMC_CRYPTO_OP) = 1; // Disable SE. + TMR(0x18C) = 0xC45A; + TMR(0x80) = 0xC0000000; + TMR(0x180) = 0x8019; + TMR(0x188) = 1; + while (1); +} + +void error(char *errStr) { + gfx_con_setcol(&gfx_con, RED, 0, 0); + print(strcat("Error: ", errStr)); + gfx_con_setcol(&gfx_con, ORANGE, 0, 0); +} \ No newline at end of file diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000..63fabe5 --- /dev/null +++ b/src/error.h @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#pragma once + +#include "hwinit/types.h" +#include "hwinit/gfx.h" + +void panic(); +void error(char *errStr); \ No newline at end of file diff --git a/src/firmware.c b/src/firmware.c new file mode 100644 index 0000000..6b4b678 --- /dev/null +++ b/src/firmware.c @@ -0,0 +1,351 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include +#include "hwinit.h" +#include "hwinit/gfx.h" +#include "fs.h" +#include "fuse.h" +#include "package.h" +#include "error.h" +#include "firmware.h" + +#define VERSION "v0.1" + +static pk11_offs *pk11Offs = NULL; +static u8 customSecmon = 0; +static u8 customWarmboot = 0; + +// TODO: Maybe find these with memsearch +static const pk11_offs _pk11_offs[] = { + { "20161121183008", 0, 0x1900, 0x3FE0, { 2, 1, 0 }, 0x4002B020, 0x8000D000, 1 }, //1.0.0 + { "20170210155124", 0, 0x1900, 0x3FE0, { 0, 1, 2 }, 0x4002D000, 0x8000D000, 1 }, //2.0.0 - 2.3.0 + { "20170519101410", 1, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, 0x8000D000, 1 }, //3.0.0 + { "20170710161758", 2, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, 0x8000D000, 1 }, //3.0.1 - 3.0.2 + { "20170921172629", 3, 0x1800, 0x3FE0, { 1, 2, 0 }, 0x4002B000, 0x4003B000, 0 }, //4.0.0 - 4.1.0 + { "20180220163747", 4, 0x1900, 0x3FE0, { 1, 2, 0 }, 0x4002B000, 0x4003B000, 0 }, //5.0.0 - 5.0.2 + { NULL, 0, 0, 0, 0 } // End. +}; + +static void SE_lock() { + for (u32 i = 0; i < 16; i++) + se_key_acc_ctrl(i, 0x15); + + for (u32 i = 0; i < 2; i++) + se_rsa_acc_ctrl(i, 1); + + SE(0x4) = 0; // Make this reg secure only. + SE(SE_KEY_TABLE_ACCESS_LOCK_OFFSET) = 0; // Make all key access regs secure only. + SE(SE_RSA_KEYTABLE_ACCESS_LOCK_OFFSET) = 0; // Make all rsa access regs secure only. + SE(SE_SECURITY_0) &= 0xFFFFFFFB; // Make access lock regs secure only. +} + +void drawSplash() { + // Draw splashscreen to framebuffer. + if(fopen("/ReiNX/splash.bin", "rb") != NULL) { + fread((void*)0xC0000000, fsize(), 1); + fclose(); + } + sleep(3000000); +} + +pk11_offs *pkg11_offsentify(u8 *pkg1) { + for (u32 i = 0; _pk11_offs[i].id; i++) + if (!memcmp(pkg1 + 0x10, _pk11_offs[i].id, 12)) + return &_pk11_offs[i]; + return NULL; +} + +static u32 calcKipSize(pkg2_kip1_t *kip1) { + u32 size = sizeof(pkg2_kip1_t); + for (u32 j = 0; j < KIP1_NUM_SECTIONS; j++) + size += kip1->sections[j].size_comp; + return size; +} + +void pkg2_parse_kips(link_t *info, pkg2_hdr_t *pkg2) { + u8 *ptr = pkg2->data + pkg2->sec_size[PKG2_SEC_KERNEL]; + pkg2_ini1_t *ini1 = (pkg2_ini1_t *)ptr; + ptr += sizeof(pkg2_ini1_t); + + for (u32 i = 0; i < ini1->num_procs; i++) { + pkg2_kip1_t *kip1 = (pkg2_kip1_t *)ptr; + pkg2_kip1_info_t *ki = (pkg2_kip1_info_t *)malloc(sizeof(pkg2_kip1_info_t)); + ki->kip1 = kip1; + ki->size = calcKipSize(kip1); + list_append(info, &ki->link); + ptr += ki->size; + } +} + +void loadKip(link_t *info, char *path) { + if(fopen(path, "rb") == NULL) return; + pkg2_kip1_t *ckip = malloc(fsize()); + fread(ckip, fsize(), 1); + fclose(); + LIST_FOREACH_ENTRY(pkg2_kip1_info_t, ki, info, link) { + if (ki->kip1->tid == ckip->tid) { + ki->kip1 = ckip; + ki->size = calcKipSize(ckip); + return; + } + } + pkg2_kip1_info_t *ki = malloc(sizeof(pkg2_kip1_info_t)); + ki->kip1 = ckip; + ki->size = calcKipSize(ckip); + list_append(info, &ki->link); +} + +void patch(pk11_offs *pk11, pkg2_hdr_t *pkg2) { + if(!customSecmon){ + uPtr *ver_ptr = NULL; + uPtr *pk21_ptr = NULL; + uPtr *hdrsig_ptr = NULL; + uPtr *sha2_ptr = NULL; + switch(pk11->kb) { + case KB_FIRMWARE_VERSION_100_200: + case KB_FIRMWARE_VERSION_300: + case KB_FIRMWARE_VERSION_301: { + u8 verPattern[] = {0x40, 0x19, 0x00, 0x36, 0x47, 0xD7, 0xFF, 0x97}; + u8 hdrSigPattern[] = {0x80, 0x1E, 0x00, 0x36, 0x6B, 0xD7, 0xFF, 0x97}; + u8 sha2Pattern[] = {0xC0, 0x18, 0x00, 0x36, 0x40, 0xD7, 0xFF, 0x97}; + u8 pk21Pattern[] = {0x40, 0x19, 0x00, 0x36, 0xE0, 0x03, 0x08, 0x91}; + + ver_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, verPattern, sizeof(verPattern)); + pk21_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, pk21Pattern, sizeof(pk21Pattern)); + hdrsig_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, hdrSigPattern, sizeof(hdrSigPattern)); + sha2_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, sha2Pattern, sizeof(sha2Pattern)); + break; + } + default: { + u8 verPattern[] = {0x00, 0x01, 0x00, 0x36, 0xFD, 0x7B, 0x41, 0xA9}; + u8 hdrSigPattern[] = {0x86, 0xFE, 0xFF, 0x97, 0x80, 0x00, 0x00, 0x36}; + u8 sha2Pattern[] = {0xF2, 0xFB, 0xFF, 0x97, 0xE0, 0x03}; + + ver_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, verPattern, sizeof(verPattern)); + pk21_ptr = (uPtr*)((u32)ver_ptr - 0xC); + hdrsig_ptr = (uPtr*)(memsearch((void *)pk11->secmon_base, 0x10000, hdrSigPattern, sizeof(hdrSigPattern)) + 4); + sha2_ptr = (uPtr*)memsearch((void *)pk11->secmon_base, 0x10000, sha2Pattern, sizeof(sha2Pattern)); + break; + } + } + *pk21_ptr = NOP; + *ver_ptr = NOP; + *hdrsig_ptr = NOP; + *sha2_ptr = NOP; + } +} + +int keygen(u8 *keyblob, u32 fwVer, void *tsec_fw) { + u8 tmp[0x10]; + + se_key_acc_ctrl(0x0D, 0x15); + se_key_acc_ctrl(0x0E, 0x15); + + // Get TSEC key. + if (tsec_query(tmp, 1, tsec_fw) < 0) + return 0; + + se_aes_key_set(0x0D, tmp, 0x10); + + // Derive keyblob keys from TSEC+SBK. + se_aes_crypt_block_ecb(0x0D, 0x00, tmp, keyblob_keyseeds[0]); + se_aes_unwrap_key(0x0F, 0x0E, tmp); + se_aes_crypt_block_ecb(0xD, 0x00, tmp, keyblob_keyseeds[fwVer]); + se_aes_unwrap_key(0x0D, 0x0E, tmp); + + // Clear SBK + se_aes_key_clear(0x0E); + + se_aes_crypt_block_ecb(0x0D, 0, tmp, cmac_keyseed); + se_aes_unwrap_key(0x0B, 0x0D, cmac_keyseed); + + // Decrypt keyblob and set keyslots. + se_aes_crypt_ctr(0x0D, keyblob + 0x20, 0x90, keyblob + 0x20, 0x90, keyblob + 0x10); + se_aes_key_set(0x0B, keyblob + 0x20 + 0x80, 0x10); // Package1 key + se_aes_key_set(0x0C, keyblob + 0x20, 0x10); + se_aes_key_set(0x0D, keyblob + 0x20, 0x10); + + se_aes_crypt_block_ecb(0x0C, 0, tmp, master_keyseed_retail); + + switch (fwVer) { + case KB_FIRMWARE_VERSION_100_200: + case KB_FIRMWARE_VERSION_300: + case KB_FIRMWARE_VERSION_301: + se_aes_unwrap_key(0x0D, 0x0F, console_keyseed); + se_aes_unwrap_key(0x0C, 0x0C, master_keyseed_retail); + break; + + case KB_FIRMWARE_VERSION_400: + se_aes_unwrap_key(0x0D, 0x0F, console_keyseed_4xx); + se_aes_unwrap_key(0x0F, 0x0F, console_keyseed); + se_aes_unwrap_key(0x0E, 0x0C, master_keyseed_4xx); + se_aes_unwrap_key(0x0C, 0x0C, master_keyseed_retail); + break; + + case KB_FIRMWARE_VERSION_500: + default: + se_aes_unwrap_key(0x0A, 0x0F, console_keyseed_4xx); + se_aes_unwrap_key(0x0F, 0x0F, console_keyseed); + se_aes_unwrap_key(0x0E, 0x0C, master_keyseed_4xx); + se_aes_unwrap_key(0x0C, 0x0C, master_keyseed_retail); + break; + } + + // Package2 key + se_key_acc_ctrl(0x08, 0x15); + se_aes_unwrap_key(0x08, 0x0C, key8_keyseed); +} + +u8 loadFirm() { + sdmmc_storage_t storage; + sdmmc_t sdmmc; + u32 ret = 0; + + sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); + sdmmc_storage_set_mmc_partition(&storage, 1); + + // Read package1. + print("Reading Package1...\n"); + u8 *package1 = (u8 *)malloc(0x40000); + sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, package1); + + // Setup firmware specific data. + pk11Offs = pkg11_offsentify(package1); + u8 *keyblob = (u8 *)malloc(NX_EMMC_BLOCKSIZE); + sdmmc_storage_read(&storage, 0x180000 / NX_EMMC_BLOCKSIZE + pk11Offs->kb, 1, keyblob); + keygen(keyblob, pk11Offs->kb, package1 + pk11Offs->tsec_off); + free(keyblob); + + // Decrypt package1 and setup warmboot. + print("Decrypting Package1...\n"); + u8 *pkg11 = package1 + pk11Offs->pkg11_off; + u32 pkg11_size = *(u32 *)pkg11; + se_aes_crypt_ctr(11, pkg11 + 0x20, pkg11_size, pkg11 + 0x20, pkg11_size, pkg11 + 0x10); + ret = pkg1_unpack(pk11Offs, package1); + customWarmboot = ret & 1; + customSecmon = ret & 2; + PMC(APBDEV_PMC_SCRATCH1) = pk11Offs->warmboot_base; + free(package1); + + // Read GPT partition. + LIST_INIT(gpt); + sdmmc_storage_set_mmc_partition(&storage, 0); + print("Parsing GPT...\n"); + nx_emmc_gpt_parse(&gpt, &storage); + emmc_part_t *pkg2_part = nx_emmc_part_find(&gpt, "BCPKG2-1-Normal-Main"); + nx_emmc_gpt_free(&gpt); + if (!pkg2_part) { + error("Failed to read GPT!\n"); + return 1; + } + + // Read Package2. + u8 *tmp = (u8 *)malloc(NX_EMMC_BLOCKSIZE); + print("Reading Package2 size...\n"); + nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, 1, tmp); + u32 *hdr = (u32 *)(tmp + 0x100); + u32 pkg2_size = hdr[0] ^ hdr[2] ^ hdr[3]; + free(tmp); + u8 *pkg2 = malloc(ALIGN(pkg2_size, NX_EMMC_BLOCKSIZE)); + print("Reading Package2...\n"); + ret = nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, ALIGN(pkg2_size, NX_EMMC_BLOCKSIZE) / NX_EMMC_BLOCKSIZE, pkg2); + sdmmc_storage_end(&storage); + if (!ret) { + error("Failed to read Package2!\n"); + return 1; + } + + // Unpack Package2. + print("Unpacking package2...\n"); + pkg2_hdr_t *dec_pkg2 = unpackFirmwarePackage(pkg2); + LIST_INIT(kip1_info); + pkg2_parse_kips(&kip1_info, dec_pkg2); + + // Patch firmware. + print("Patching OS...\n"); + patch(pk11Offs, dec_pkg2); + + // Load all KIPs. + char **sysmods = NULL; + size_t cnt = enumerateDir(&sysmods, "/ReiNX/sysmodules", "*.kip"); + for (u32 i = 0; i < cnt ; i++) { + print("%kLoading %s\n%k", YELLOW, sysmods[i], ORANGE); + loadKip(&kip1_info, sysmods[i]); + free(sysmods[i]); + } + free(sysmods); + + // Build Package2. + buildFirmwarePackage(dec_pkg2->data, dec_pkg2->sec_size[PKG2_SEC_KERNEL], &kip1_info); +} + +void launch() { + u8 pre4x = pk11Offs->kb < KB_FIRMWARE_VERSION_400; + + se_aes_key_clear(0x8); + se_aes_key_clear(0xB); + + if (pre4x) { + se_key_acc_ctrl(12, 0xFF); + se_key_acc_ctrl(13, 0xFF); + } else { + se_key_acc_ctrl(12, 0xFF); + se_key_acc_ctrl(15, 0xFF); + } + + // TODO: Don't Clear 'BootConfig' for retail >1.0.0. + //memset((void *)0x4003D000, 0, 0x3000); + + SE_lock(); + + // Start boot process now that pk21 is loaded. + *BOOT_STATE_ADDR = (pre4x ? BOOT_PKG2_LOADED : BOOT_PKG2_LOADED_4X); + + // Boot secmon and Wait for it get ready. + cluster_boot_cpu0(pk11Offs->secmon_base); + while (!*SECMON_STATE_ADDR) + sleep(1); + + // Disable display. + if (pre4x) + display_end(); + + // Signal to finish boot process. + *BOOT_STATE_ADDR = (pre4x ? BOOT_DONE : BOOT_DONE_4X);; + + // Halt ourselves in waitevent state. + while (1) FLOW_CTLR(0x4) = 0x50000000; +} + +void firmware() { + display_init(); + gfx_init_ctxt(&gfx_ctxt, display_init_framebuffer(), 720, 1280, 768); + gfx_clear_color(&gfx_ctxt, 0xFF000000); + gfx_con_init(&gfx_con, &gfx_ctxt); + gfx_con_setcol(&gfx_con, ORANGE, 0, 0); + + if (!sd_mount()) { + error("Failed to init SD card!\n"); + return; + } + + print("Welcome to ReiNX %s!\n", VERSION); + loadFirm(); + drawSplash(); + launch(); +} \ No newline at end of file diff --git a/src/firmware.h b/src/firmware.h new file mode 100644 index 0000000..07cc51a --- /dev/null +++ b/src/firmware.h @@ -0,0 +1,17 @@ +#pragma once + +#include "hwinit/types.h" +//Boot status +#define BOOT_STATE_ADDR (vu32 *)0x40002EF8 +#define SECMON_STATE_ADDR (vu32 *)0x40002EFC + +#define BOOT_PKG2_LOADED 2 +#define BOOT_DONE 3 + +#define BOOT_PKG2_LOADED_4X 3 +#define BOOT_DONE_4X 4 + +//Instructions +#define NOP 0xD503201F + +void firmware(); \ No newline at end of file diff --git a/src/fs.c b/src/fs.c new file mode 100644 index 0000000..ee92fac --- /dev/null +++ b/src/fs.c @@ -0,0 +1,116 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include +#include "hwinit.h" +#include "hwinit/gfx.h" +#include "hwinit/ff.h" +#include "error.h" +#include "fs.h" + +sdmmc_t sd_sdmmc; +sdmmc_storage_t sd_storage; +FATFS sd_fs; +int sd_mounted; +FIL fp; + +u32 sd_mount() { + if (sd_mounted) return 1; + + if (sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11) && f_mount(&sd_fs, "", 1) == FR_OK) { + sd_mounted = 1; + return 1; + } + + return 0; +} + +u32 fopen(const char *path, const char *mode) { + if (f_open(&fp, path, mode[0] == 'w' ? FA_WRITE : FA_READ) != FR_OK) + return NULL; + return 1; +} + +u32 fread(void *buf, size_t size, size_t ntimes) { + u8 *ptr = buf; + while (size > 0) { + u32 rsize = MIN(ntimes * size, size); + if (f_read(&fp, ptr, rsize, NULL) != FR_OK) { + error("Failed read!\n"); + return NULL; + } + + ptr += rsize; + size -= rsize; + } + return 1; +} + +u32 fwrite(void *buf, size_t size, size_t ntimes) { + u8 *ptr = buf; + while (size > 0) { + u32 rsize = MIN(ntimes * size, size); + if (f_write(&fp, ptr, rsize, NULL) != FR_OK) { + error("Failed write!\n"); + return NULL; + } + + ptr += rsize; + size -= rsize; + } + return 1; +} + +size_t fsize() { + return f_size(&fp); +} + +void fclose() { + f_close(&fp); +} + +size_t enumerateDir(char ***output, char *path, char *pattern) { + DIR dp; + FILINFO fno; + FRESULT fr; + char **out = NULL; + size_t pathlen = strlen(path); + + if (pathlen >= FF_LFN_BUF) + goto end; + + fr = f_findfirst(&dp, &fno, path, pattern); + + char pathb[FF_LFN_BUF] = {0}; + strcpy(pathb, path); + pathb[pathlen] = '/'; + + int i; for (i = 0; fno.fname[0] != 0 && fr == FR_OK; i++) { + out = (char **)realloc(out, (i+1) * sizeof(char *)); + out[i] = (char *)malloc(FF_LFN_BUF); + strcpy(out[i], pathb); + strcat(out[i], fno.fname); + pathb[pathlen+1] = 0; + f_findnext(&dp, &fno); + } + + end: + f_closedir(&dp); + *output = out; + return i; +} \ No newline at end of file diff --git a/src/fs.h b/src/fs.h new file mode 100644 index 0000000..e8e7c4d --- /dev/null +++ b/src/fs.h @@ -0,0 +1,9 @@ +#pragma once + +u32 sd_mount(); +u32 fopen(const char *path, const char *mode); +u32 fread(void *buf, size_t size, size_t ntimes); +u32 fwrite(void *buf, size_t size, size_t ntimes); +size_t fsize(); +void fclose(); +size_t enumerateDir(char ***output, char *path, char *pattern); \ No newline at end of file diff --git a/src/fuse.c b/src/fuse.c new file mode 100644 index 0000000..0239048 --- /dev/null +++ b/src/fuse.c @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "hwinit/t210.h" +#include "fuse.h" + +u32 get_unknown_config() { + u32 res = 0; + u32 deviceInfo = FUSE(FUSE_RESERVED_ODMX(4)); + u32 config = ((deviceInfo & 4u) >> 2) | 2 * ((deviceInfo & 0x100u) >> 8); + + if(config == 1) + return 0; + if(config == 2) + return 1; + if(config || (res = FUSE(FUSE_SPARE_BIT_5)) != 0) + res = 3; + return res; +} + +u32 get_unit_type() { + u32 deviceInfo = FUSE(FUSE_RESERVED_ODMX(4)); + u32 deviceType = deviceInfo & 3 | 4 * ((deviceInfo & 0x200u) >> 9); + + if(deviceType == 3) + return 0; + if(deviceType == 4) + return 1; + return 2; +} + +u32 master_key_ver() { + return FUSE(FUSE_SPARE_BIT_5) & 1; +} \ No newline at end of file diff --git a/src/fuse.h b/src/fuse.h new file mode 100644 index 0000000..0d3640e --- /dev/null +++ b/src/fuse.h @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#pragma once + +u32 get_unknown_config(); +u32 get_unit_type(); +u32 master_key_ver(); \ No newline at end of file diff --git a/src/hwinit.h b/src/hwinit.h new file mode 100644 index 0000000..69f2324 --- /dev/null +++ b/src/hwinit.h @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#pragma once + +#include +#include "hwinit/btn.h" +#include "hwinit/clock.h" +#include "hwinit/cluster.h" +#include "hwinit/uart.h" +#include "hwinit/i2c.h" +#include "hwinit/sdram.h" +#include "hwinit/di.h" +#include "hwinit/mc.h" +#include "hwinit/t210.h" +#include "hwinit/pmc.h" +#include "hwinit/pinmux.h" +#include "hwinit/util.h" +#include "hwinit/tsec.h" +#include "hwinit/kfuse.h" +#include "hwinit/max77620.h" +#include "hwinit/max7762x.h" +#include "hwinit/gpio.h" +#include "hwinit/sdmmc.h" +#include "hwinit/heap.h" +#include "hwinit/list.h" +#include "hwinit/nx_emmc.h" +#include "hwinit/se.h" +#include "hwinit/se_t210.h" +#include "hwinit/mmc.h" + +#define NUM_KEYBLOB_KEYS 5 +static const u8 keyblob_keyseeds[NUM_KEYBLOB_KEYS][0x10] = { + { 0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3 }, //1.0.0 + { 0x0C, 0x25, 0x61, 0x5D, 0x68, 0x4C, 0xEB, 0x42, 0x1C, 0x23, 0x79, 0xEA, 0x82, 0x25, 0x12, 0xAC }, //3.0.0 + { 0x33, 0x76, 0x85, 0xEE, 0x88, 0x4A, 0xAE, 0x0A, 0xC2, 0x8A, 0xFD, 0x7D, 0x63, 0xC0, 0x43, 0x3B }, //3.0.1 + { 0x2D, 0x1F, 0x48, 0x80, 0xED, 0xEC, 0xED, 0x3E, 0x3C, 0xF2, 0x48, 0xB5, 0x65, 0x7D, 0xF7, 0xBE }, //4.0.0 + { 0xBB, 0x5A, 0x01, 0xF9, 0x88, 0xAF, 0xF5, 0xFC, 0x6C, 0xFF, 0x07, 0x9E, 0x13, 0x3C, 0x39, 0x80 } //5.0.0 +}; + +static const u8 cmac_keyseed[0x10] = { 0x59, 0xC7, 0xFB, 0x6F, 0xBE, 0x9B, 0xBE, 0x87, 0x65, 0x6B, 0x15, 0xC0, 0x53, 0x73, 0x36, 0xA5 }; +static const u8 master_keyseed_retail[0x10] = { 0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C }; +static const u8 console_keyseed[0x10] = { 0x4F, 0x02, 0x5F, 0x0E, 0xB6, 0x6D, 0x11, 0x0E, 0xDC, 0x32, 0x7D, 0x41, 0x86, 0xC2, 0xF4, 0x78 }; +static const u8 key8_keyseed[] = { 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 }; +static const u8 master_keyseed_4xx[0x10] = { 0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66 }; +static const u8 console_keyseed_4xx[0x10] = { 0x0C, 0x91, 0x09, 0xDB, 0x93, 0x93, 0x07, 0x81, 0x07, 0x3C, 0xC4, 0x16, 0x22, 0x7C, 0x6C, 0x28 }; \ No newline at end of file diff --git a/src/hwinit/ELF.h b/src/hwinit/ELF.h new file mode 100644 index 0000000..2af0859 --- /dev/null +++ b/src/hwinit/ELF.h @@ -0,0 +1,1667 @@ +//===-- llvm/Support/ELF.h - ELF constants and data structures --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header contains common, non-processor-specific data structures and +// constants for the ELF file format. +// +// The details of the ELF32 bits in this file are largely based on the Tool +// Interface Standard (TIS) Executable and Linking Format (ELF) Specification +// Version 1.2, May 1995. The ELF64 stuff is based on ELF-64 Object File Format +// Version 1.5, Draft 2, May 1998 as well as OpenBSD header files. +// +//===----------------------------------------------------------------------===// + +#ifndef _ELF_H_ +#define _ELF_H_ + +#include "types.h" + +typedef u32 Elf32_Addr; // Program address +typedef u32 Elf32_Off; // File offset +typedef u16 Elf32_Half; +typedef u32 Elf32_Word; +typedef s32 Elf32_Sword; + +typedef u64 Elf64_Addr; +typedef u64 Elf64_Off; +typedef u16 Elf64_Half; +typedef u32 Elf64_Word; +typedef s32 Elf64_Sword; +typedef u64 Elf64_Xword; +typedef s64 Elf64_Sxword; + +// Object file magic string. +static const char ElfMagic[] = {0x7f, 'E', 'L', 'F'}; + +// e_ident size and indices. +enum +{ + EI_MAG0 = 0, // File identification index. + EI_MAG1 = 1, // File identification index. + EI_MAG2 = 2, // File identification index. + EI_MAG3 = 3, // File identification index. + EI_CLASS = 4, // File class. + EI_DATA = 5, // Data encoding. + EI_VERSION = 6, // File version. + EI_OSABI = 7, // OS/ABI identification. + EI_ABIVERSION = 8, // ABI version. + EI_PAD = 9, // Start of padding bytes. + EI_NIDENT = 16 // Number of bytes in e_ident. +}; + +struct Elf32_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; // ELF Identification bytes + Elf32_Half e_type; // Type of file (see ET_* below) + Elf32_Half e_machine; // Required architecture for this file (see EM_*) + Elf32_Word e_version; // Must be equal to 1 + Elf32_Addr e_entry; // Address to jump to in order to start program + Elf32_Off e_phoff; // Program header table's file offset, in bytes + Elf32_Off e_shoff; // Section header table's file offset, in bytes + Elf32_Word e_flags; // Processor-specific flags + Elf32_Half e_ehsize; // Size of ELF header, in bytes + Elf32_Half e_phentsize; // Size of an entry in the program header table + Elf32_Half e_phnum; // Number of entries in the program header table + Elf32_Half e_shentsize; // Size of an entry in the section header table + Elf32_Half e_shnum; // Number of entries in the section header table + Elf32_Half e_shstrndx; // Sect hdr table index of sect name string table +}; + +// 64-bit ELF header. Fields are the same as for ELF32, but with different +// types (see above). +struct Elf64_Ehdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +}; + +// File types +enum +{ + ET_NONE = 0, // No file type + ET_REL = 1, // Relocatable file + ET_EXEC = 2, // Executable file + ET_DYN = 3, // Shared object file + ET_CORE = 4, // Core file + ET_LOPROC = 0xff00, // Beginning of processor-specific codes + ET_HIPROC = 0xffff // Processor-specific +}; + +// Versioning +enum +{ + EV_NONE = 0, + EV_CURRENT = 1 +}; + +// Machine architectures +enum +{ + EM_NONE = 0, // No machine + EM_M32 = 1, // AT&T WE 32100 + EM_SPARC = 2, // SPARC + EM_386 = 3, // Intel 386 + EM_68K = 4, // Motorola 68000 + EM_88K = 5, // Motorola 88000 + EM_486 = 6, // Intel 486 (deprecated) + EM_860 = 7, // Intel 80860 + EM_MIPS = 8, // MIPS R3000 + EM_S370 = 9, // IBM System/370 + EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian + EM_PARISC = 15, // Hewlett-Packard PA-RISC + EM_VPP500 = 17, // Fujitsu VPP500 + EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC + EM_960 = 19, // Intel 80960 + EM_PPC = 20, // PowerPC + EM_PPC64 = 21, // PowerPC64 + EM_S390 = 22, // IBM System/390 + EM_SPU = 23, // IBM SPU/SPC + EM_V800 = 36, // NEC V800 + EM_FR20 = 37, // Fujitsu FR20 + EM_RH32 = 38, // TRW RH-32 + EM_RCE = 39, // Motorola RCE + EM_ARM = 40, // ARM + EM_ALPHA = 41, // DEC Alpha + EM_SH = 42, // Hitachi SH + EM_SPARCV9 = 43, // SPARC V9 + EM_TRICORE = 44, // Siemens TriCore + EM_ARC = 45, // Argonaut RISC Core + EM_H8_300 = 46, // Hitachi H8/300 + EM_H8_300H = 47, // Hitachi H8/300H + EM_H8S = 48, // Hitachi H8S + EM_H8_500 = 49, // Hitachi H8/500 + EM_IA_64 = 50, // Intel IA-64 processor architecture + EM_MIPS_X = 51, // Stanford MIPS-X + EM_COLDFIRE = 52, // Motorola ColdFire + EM_68HC12 = 53, // Motorola M68HC12 + EM_MMA = 54, // Fujitsu MMA Multimedia Accelerator + EM_PCP = 55, // Siemens PCP + EM_NCPU = 56, // Sony nCPU embedded RISC processor + EM_NDR1 = 57, // Denso NDR1 microprocessor + EM_STARCORE = 58, // Motorola Star*Core processor + EM_ME16 = 59, // Toyota ME16 processor + EM_ST100 = 60, // STMicroelectronics ST100 processor + EM_TINYJ = 61, // Advanced Logic Corp. TinyJ embedded processor family + EM_X86_64 = 62, // AMD x86-64 architecture + EM_PDSP = 63, // Sony DSP Processor + EM_PDP10 = 64, // Digital Equipment Corp. PDP-10 + EM_PDP11 = 65, // Digital Equipment Corp. PDP-11 + EM_FX66 = 66, // Siemens FX66 microcontroller + EM_ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller + EM_ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller + EM_68HC16 = 69, // Motorola MC68HC16 Microcontroller + EM_68HC11 = 70, // Motorola MC68HC11 Microcontroller + EM_68HC08 = 71, // Motorola MC68HC08 Microcontroller + EM_68HC05 = 72, // Motorola MC68HC05 Microcontroller + EM_SVX = 73, // Silicon Graphics SVx + EM_ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller + EM_VAX = 75, // Digital VAX + EM_CRIS = 76, // Axis Communications 32-bit embedded processor + EM_JAVELIN = 77, // Infineon Technologies 32-bit embedded processor + EM_FIREPATH = 78, // Element 14 64-bit DSP Processor + EM_ZSP = 79, // LSI Logic 16-bit DSP Processor + EM_MMIX = 80, // Donald Knuth's educational 64-bit processor + EM_HUANY = 81, // Harvard University machine-independent object files + EM_PRISM = 82, // SiTera Prism + EM_AVR = 83, // Atmel AVR 8-bit microcontroller + EM_FR30 = 84, // Fujitsu FR30 + EM_D10V = 85, // Mitsubishi D10V + EM_D30V = 86, // Mitsubishi D30V + EM_V850 = 87, // NEC v850 + EM_M32R = 88, // Mitsubishi M32R + EM_MN10300 = 89, // Matsushita MN10300 + EM_MN10200 = 90, // Matsushita MN10200 + EM_PJ = 91, // picoJava + EM_OPENRISC = 92, // OpenRISC 32-bit embedded processor + EM_ARC_COMPACT = 93, // ARC International ARCompact processor (old + // spelling/synonym: EM_ARC_A5) + EM_XTENSA = 94, // Tensilica Xtensa Architecture + EM_VIDEOCORE = 95, // Alphamosaic VideoCore processor + EM_TMM_GPP = 96, // Thompson Multimedia General Purpose Processor + EM_NS32K = 97, // National Semiconductor 32000 series + EM_TPC = 98, // Tenor Network TPC processor + EM_SNP1K = 99, // Trebia SNP 1000 processor + EM_ST200 = 100, // STMicroelectronics (www.st.com) ST200 + EM_IP2K = 101, // Ubicom IP2xxx microcontroller family + EM_MAX = 102, // MAX Processor + EM_CR = 103, // National Semiconductor CompactRISC microprocessor + EM_F2MC16 = 104, // Fujitsu F2MC16 + EM_MSP430 = 105, // Texas Instruments embedded microcontroller msp430 + EM_BLACKFIN = 106, // Analog Devices Blackfin (DSP) processor + EM_SE_C33 = 107, // S1C33 Family of Seiko Epson processors + EM_SEP = 108, // Sharp embedded microprocessor + EM_ARCA = 109, // Arca RISC Microprocessor + EM_UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC + // of Peking University + EM_EXCESS = 111, // eXcess: 16/32/64-bit configurable embedded CPU + EM_DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor + EM_ALTERA_NIOS2 = 113, // Altera Nios II soft-core processor + EM_CRX = 114, // National Semiconductor CompactRISC CRX + EM_XGATE = 115, // Motorola XGATE embedded processor + EM_C166 = 116, // Infineon C16x/XC16x processor + EM_M16C = 117, // Renesas M16C series microprocessors + EM_DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal + // Controller + EM_CE = 119, // Freescale Communication Engine RISC core + EM_M32C = 120, // Renesas M32C series microprocessors + EM_TSK3000 = 131, // Altium TSK3000 core + EM_RS08 = 132, // Freescale RS08 embedded processor + EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP + // processors + EM_ECOG2 = 134, // Cyan Technology eCOG2 microprocessor + EM_SCORE7 = 135, // Sunplus S+core7 RISC processor + EM_DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor + EM_VIDEOCORE3 = 137, // Broadcom VideoCore III processor + EM_LATTICEMICO32 = 138, // RISC processor for Lattice FPGA architecture + EM_SE_C17 = 139, // Seiko Epson C17 family + EM_TI_C6000 = 140, // The Texas Instruments TMS320C6000 DSP family + EM_TI_C2000 = 141, // The Texas Instruments TMS320C2000 DSP family + EM_TI_C5500 = 142, // The Texas Instruments TMS320C55x DSP family + EM_MMDSP_PLUS = 160, // STMicroelectronics 64bit VLIW Data Signal Processor + EM_CYPRESS_M8C = 161, // Cypress M8C microprocessor + EM_R32C = 162, // Renesas R32C series microprocessors + EM_TRIMEDIA = 163, // NXP Semiconductors TriMedia architecture family + EM_HEXAGON = 164, // Qualcomm Hexagon processor + EM_8051 = 165, // Intel 8051 and variants + EM_STXP7X = 166, // STMicroelectronics STxP7x family of configurable + // and extensible RISC processors + EM_NDS32 = 167, // Andes Technology compact code size embedded RISC + // processor family + EM_ECOG1 = 168, // Cyan Technology eCOG1X family + EM_ECOG1X = 168, // Cyan Technology eCOG1X family + EM_MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers + EM_XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor + EM_MANIK = 171, // M2000 Reconfigurable RISC Microprocessor + EM_CRAYNV2 = 172, // Cray Inc. NV2 vector architecture + EM_RX = 173, // Renesas RX family + EM_METAG = 174, // Imagination Technologies META processor + // architecture + EM_MCST_ELBRUS = 175, // MCST Elbrus general purpose hardware architecture + EM_ECOG16 = 176, // Cyan Technology eCOG16 family + EM_CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit + // microprocessor + EM_ETPU = 178, // Freescale Extended Time Processing Unit + EM_SLE9X = 179, // Infineon Technologies SLE9X core + EM_L10M = 180, // Intel L10M + EM_K10M = 181, // Intel K10M + EM_AARCH64 = 183, // ARM AArch64 + EM_AVR32 = 185, // Atmel Corporation 32-bit microprocessor family + EM_STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller + EM_TILE64 = 187, // Tilera TILE64 multicore architecture family + EM_TILEPRO = 188, // Tilera TILEPro multicore architecture family + EM_CUDA = 190, // NVIDIA CUDA architecture + EM_TILEGX = 191, // Tilera TILE-Gx multicore architecture family + EM_CLOUDSHIELD = 192, // CloudShield architecture family + EM_COREA_1ST = 193, // KIPO-KAIST Core-A 1st generation processor family + EM_COREA_2ND = 194, // KIPO-KAIST Core-A 2nd generation processor family + EM_ARC_COMPACT2 = 195, // Synopsys ARCompact V2 + EM_OPEN8 = 196, // Open8 8-bit RISC soft processor core + EM_RL78 = 197, // Renesas RL78 family + EM_VIDEOCORE5 = 198, // Broadcom VideoCore V processor + EM_78KOR = 199, // Renesas 78KOR family + EM_56800EX = 200 // Freescale 56800EX Digital Signal Controller (DSC) +}; + +// Object file classes. +enum +{ + ELFCLASSNONE = 0, + ELFCLASS32 = 1, // 32-bit object file + ELFCLASS64 = 2 // 64-bit object file +}; + +// Object file byte orderings. +enum +{ + ELFDATANONE = 0, // Invalid data encoding. + ELFDATA2LSB = 1, // Little-endian object file + ELFDATA2MSB = 2 // Big-endian object file +}; + +// OS ABI identification. +enum +{ + ELFOSABI_NONE = 0, // UNIX System V ABI + ELFOSABI_HPUX = 1, // HP-UX operating system + ELFOSABI_NETBSD = 2, // NetBSD + ELFOSABI_GNU = 3, // GNU/Linux + ELFOSABI_LINUX = 3, // Historical alias for ELFOSABI_GNU. + ELFOSABI_HURD = 4, // GNU/Hurd + ELFOSABI_SOLARIS = 6, // Solaris + ELFOSABI_AIX = 7, // AIX + ELFOSABI_IRIX = 8, // IRIX + ELFOSABI_FREEBSD = 9, // FreeBSD + ELFOSABI_TRU64 = 10, // TRU64 UNIX + ELFOSABI_MODESTO = 11, // Novell Modesto + ELFOSABI_OPENBSD = 12, // OpenBSD + ELFOSABI_OPENVMS = 13, // OpenVMS + ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel + ELFOSABI_AROS = 15, // AROS + ELFOSABI_FENIXOS = 16, // FenixOS + ELFOSABI_C6000_ELFABI = 64, // Bare-metal TMS320C6000 + ELFOSABI_C6000_LINUX = 65, // Linux TMS320C6000 + ELFOSABI_ARM = 97, // ARM + ELFOSABI_STANDALONE = 255 // Standalone (embedded) application +}; + +// X86_64 relocations. +enum +{ + R_X86_64_NONE = 0, + R_X86_64_64 = 1, + R_X86_64_PC32 = 2, + R_X86_64_GOT32 = 3, + R_X86_64_PLT32 = 4, + R_X86_64_COPY = 5, + R_X86_64_GLOB_DAT = 6, + R_X86_64_JUMP_SLOT = 7, + R_X86_64_RELATIVE = 8, + R_X86_64_GOTPCREL = 9, + R_X86_64_32 = 10, + R_X86_64_32S = 11, + R_X86_64_16 = 12, + R_X86_64_PC16 = 13, + R_X86_64_8 = 14, + R_X86_64_PC8 = 15, + R_X86_64_DTPMOD64 = 16, + R_X86_64_DTPOFF64 = 17, + R_X86_64_TPOFF64 = 18, + R_X86_64_TLSGD = 19, + R_X86_64_TLSLD = 20, + R_X86_64_DTPOFF32 = 21, + R_X86_64_GOTTPOFF = 22, + R_X86_64_TPOFF32 = 23, + R_X86_64_PC64 = 24, + R_X86_64_GOTOFF64 = 25, + R_X86_64_GOTPC32 = 26, + R_X86_64_GOT64 = 27, + R_X86_64_GOTPCREL64 = 28, + R_X86_64_GOTPC64 = 29, + R_X86_64_GOTPLT64 = 30, + R_X86_64_PLTOFF64 = 31, + R_X86_64_SIZE32 = 32, + R_X86_64_SIZE64 = 33, + R_X86_64_GOTPC32_TLSDESC = 34, + R_X86_64_TLSDESC_CALL = 35, + R_X86_64_TLSDESC = 36, + R_X86_64_IRELATIVE = 37 +}; + +// i386 relocations. +// TODO: this is just a subset +enum +{ + R_386_NONE = 0, + R_386_32 = 1, + R_386_PC32 = 2, + R_386_GOT32 = 3, + R_386_PLT32 = 4, + R_386_COPY = 5, + R_386_GLOB_DAT = 6, + R_386_JUMP_SLOT = 7, + R_386_RELATIVE = 8, + R_386_GOTOFF = 9, + R_386_GOTPC = 10, + R_386_32PLT = 11, + R_386_TLS_TPOFF = 14, + R_386_TLS_IE = 15, + R_386_TLS_GOTIE = 16, + R_386_TLS_LE = 17, + R_386_TLS_GD = 18, + R_386_TLS_LDM = 19, + R_386_16 = 20, + R_386_PC16 = 21, + R_386_8 = 22, + R_386_PC8 = 23, + R_386_TLS_GD_32 = 24, + R_386_TLS_GD_PUSH = 25, + R_386_TLS_GD_CALL = 26, + R_386_TLS_GD_POP = 27, + R_386_TLS_LDM_32 = 28, + R_386_TLS_LDM_PUSH = 29, + R_386_TLS_LDM_CALL = 30, + R_386_TLS_LDM_POP = 31, + R_386_TLS_LDO_32 = 32, + R_386_TLS_IE_32 = 33, + R_386_TLS_LE_32 = 34, + R_386_TLS_DTPMOD32 = 35, + R_386_TLS_DTPOFF32 = 36, + R_386_TLS_TPOFF32 = 37, + R_386_TLS_GOTDESC = 39, + R_386_TLS_DESC_CALL = 40, + R_386_TLS_DESC = 41, + R_386_IRELATIVE = 42, + R_386_NUM = 43 +}; + +// ELF Relocation types for PPC32 +enum +{ + R_PPC_NONE = 0, /* No relocation. */ + R_PPC_ADDR32 = 1, + R_PPC_ADDR24 = 2, + R_PPC_ADDR16 = 3, + R_PPC_ADDR16_LO = 4, + R_PPC_ADDR16_HI = 5, + R_PPC_ADDR16_HA = 6, + R_PPC_ADDR14 = 7, + R_PPC_ADDR14_BRTAKEN = 8, + R_PPC_ADDR14_BRNTAKEN = 9, + R_PPC_REL24 = 10, + R_PPC_REL14 = 11, + R_PPC_REL14_BRTAKEN = 12, + R_PPC_REL14_BRNTAKEN = 13, + R_PPC_GOT16 = 14, + R_PPC_GOT16_LO = 15, + R_PPC_GOT16_HI = 16, + R_PPC_GOT16_HA = 17, + R_PPC_REL32 = 26, + R_PPC_TLS = 67, + R_PPC_DTPMOD32 = 68, + R_PPC_TPREL16 = 69, + R_PPC_TPREL16_LO = 70, + R_PPC_TPREL16_HI = 71, + R_PPC_TPREL16_HA = 72, + R_PPC_TPREL32 = 73, + R_PPC_DTPREL16 = 74, + R_PPC_DTPREL16_LO = 75, + R_PPC_DTPREL16_HI = 76, + R_PPC_DTPREL16_HA = 77, + R_PPC_DTPREL32 = 78, + R_PPC_GOT_TLSGD16 = 79, + R_PPC_GOT_TLSGD16_LO = 80, + R_PPC_GOT_TLSGD16_HI = 81, + R_PPC_GOT_TLSGD16_HA = 82, + R_PPC_GOT_TLSLD16 = 83, + R_PPC_GOT_TLSLD16_LO = 84, + R_PPC_GOT_TLSLD16_HI = 85, + R_PPC_GOT_TLSLD16_HA = 86, + R_PPC_GOT_TPREL16 = 87, + R_PPC_GOT_TPREL16_LO = 88, + R_PPC_GOT_TPREL16_HI = 89, + R_PPC_GOT_TPREL16_HA = 90, + R_PPC_GOT_DTPREL16 = 91, + R_PPC_GOT_DTPREL16_LO = 92, + R_PPC_GOT_DTPREL16_HI = 93, + R_PPC_GOT_DTPREL16_HA = 94, + R_PPC_TLSGD = 95, + R_PPC_TLSLD = 96, + R_PPC_REL16 = 249, + R_PPC_REL16_LO = 250, + R_PPC_REL16_HI = 251, + R_PPC_REL16_HA = 252 +}; + +// ELF Relocation types for PPC64 +enum +{ + R_PPC64_NONE = 0, + R_PPC64_ADDR32 = 1, + R_PPC64_ADDR24 = 2, + R_PPC64_ADDR16 = 3, + R_PPC64_ADDR16_LO = 4, + R_PPC64_ADDR16_HI = 5, + R_PPC64_ADDR16_HA = 6, + R_PPC64_ADDR14 = 7, + R_PPC64_ADDR14_BRTAKEN = 8, + R_PPC64_ADDR14_BRNTAKEN = 9, + R_PPC64_REL24 = 10, + R_PPC64_REL14 = 11, + R_PPC64_REL14_BRTAKEN = 12, + R_PPC64_REL14_BRNTAKEN = 13, + R_PPC64_GOT16 = 14, + R_PPC64_GOT16_LO = 15, + R_PPC64_GOT16_HI = 16, + R_PPC64_GOT16_HA = 17, + R_PPC64_REL32 = 26, + R_PPC64_ADDR64 = 38, + R_PPC64_ADDR16_HIGHER = 39, + R_PPC64_ADDR16_HIGHERA = 40, + R_PPC64_ADDR16_HIGHEST = 41, + R_PPC64_ADDR16_HIGHESTA = 42, + R_PPC64_REL64 = 44, + R_PPC64_TOC16 = 47, + R_PPC64_TOC16_LO = 48, + R_PPC64_TOC16_HI = 49, + R_PPC64_TOC16_HA = 50, + R_PPC64_TOC = 51, + R_PPC64_ADDR16_DS = 56, + R_PPC64_ADDR16_LO_DS = 57, + R_PPC64_GOT16_DS = 58, + R_PPC64_GOT16_LO_DS = 59, + R_PPC64_TOC16_DS = 63, + R_PPC64_TOC16_LO_DS = 64, + R_PPC64_TLS = 67, + R_PPC64_DTPMOD64 = 68, + R_PPC64_TPREL16 = 69, + R_PPC64_TPREL16_LO = 70, + R_PPC64_TPREL16_HI = 71, + R_PPC64_TPREL16_HA = 72, + R_PPC64_TPREL64 = 73, + R_PPC64_DTPREL16 = 74, + R_PPC64_DTPREL16_LO = 75, + R_PPC64_DTPREL16_HI = 76, + R_PPC64_DTPREL16_HA = 77, + R_PPC64_DTPREL64 = 78, + R_PPC64_GOT_TLSGD16 = 79, + R_PPC64_GOT_TLSGD16_LO = 80, + R_PPC64_GOT_TLSGD16_HI = 81, + R_PPC64_GOT_TLSGD16_HA = 82, + R_PPC64_GOT_TLSLD16 = 83, + R_PPC64_GOT_TLSLD16_LO = 84, + R_PPC64_GOT_TLSLD16_HI = 85, + R_PPC64_GOT_TLSLD16_HA = 86, + R_PPC64_GOT_TPREL16_DS = 87, + R_PPC64_GOT_TPREL16_LO_DS = 88, + R_PPC64_GOT_TPREL16_HI = 89, + R_PPC64_GOT_TPREL16_HA = 90, + R_PPC64_GOT_DTPREL16_DS = 91, + R_PPC64_GOT_DTPREL16_LO_DS = 92, + R_PPC64_GOT_DTPREL16_HI = 93, + R_PPC64_GOT_DTPREL16_HA = 94, + R_PPC64_TPREL16_DS = 95, + R_PPC64_TPREL16_LO_DS = 96, + R_PPC64_TPREL16_HIGHER = 97, + R_PPC64_TPREL16_HIGHERA = 98, + R_PPC64_TPREL16_HIGHEST = 99, + R_PPC64_TPREL16_HIGHESTA = 100, + R_PPC64_DTPREL16_DS = 101, + R_PPC64_DTPREL16_LO_DS = 102, + R_PPC64_DTPREL16_HIGHER = 103, + R_PPC64_DTPREL16_HIGHERA = 104, + R_PPC64_DTPREL16_HIGHEST = 105, + R_PPC64_DTPREL16_HIGHESTA = 106, + R_PPC64_TLSGD = 107, + R_PPC64_TLSLD = 108, + R_PPC64_REL16 = 249, + R_PPC64_REL16_LO = 250, + R_PPC64_REL16_HI = 251, + R_PPC64_REL16_HA = 252 +}; + +// ELF Relocation types for AArch64 + +enum +{ + R_AARCH64_NONE = 0x100, + + R_AARCH64_ABS64 = 0x101, + R_AARCH64_ABS32 = 0x102, + R_AARCH64_ABS16 = 0x103, + R_AARCH64_PREL64 = 0x104, + R_AARCH64_PREL32 = 0x105, + R_AARCH64_PREL16 = 0x106, + + R_AARCH64_MOVW_UABS_G0 = 0x107, + R_AARCH64_MOVW_UABS_G0_NC = 0x108, + R_AARCH64_MOVW_UABS_G1 = 0x109, + R_AARCH64_MOVW_UABS_G1_NC = 0x10a, + R_AARCH64_MOVW_UABS_G2 = 0x10b, + R_AARCH64_MOVW_UABS_G2_NC = 0x10c, + R_AARCH64_MOVW_UABS_G3 = 0x10d, + R_AARCH64_MOVW_SABS_G0 = 0x10e, + R_AARCH64_MOVW_SABS_G1 = 0x10f, + R_AARCH64_MOVW_SABS_G2 = 0x110, + + R_AARCH64_LD_PREL_LO19 = 0x111, + R_AARCH64_ADR_PREL_LO21 = 0x112, + R_AARCH64_ADR_PREL_PG_HI21 = 0x113, + R_AARCH64_ADD_ABS_LO12_NC = 0x115, + R_AARCH64_LDST8_ABS_LO12_NC = 0x116, + + R_AARCH64_TSTBR14 = 0x117, + R_AARCH64_CONDBR19 = 0x118, + R_AARCH64_JUMP26 = 0x11a, + R_AARCH64_CALL26 = 0x11b, + + R_AARCH64_LDST16_ABS_LO12_NC = 0x11c, + R_AARCH64_LDST32_ABS_LO12_NC = 0x11d, + R_AARCH64_LDST64_ABS_LO12_NC = 0x11e, + + R_AARCH64_LDST128_ABS_LO12_NC = 0x12b, + + R_AARCH64_ADR_GOT_PAGE = 0x137, + R_AARCH64_LD64_GOT_LO12_NC = 0x138, + + R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 0x20b, + R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 0x20c, + R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 0x20d, + R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 0x20e, + R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 0x20f, + R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 0x210, + R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 0x211, + R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 0x212, + R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 0x213, + R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 0x214, + R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 0x215, + R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 0x216, + R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 0x217, + R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 0x218, + R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 0x219, + R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 0x21a, + + R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 0x21b, + R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 0x21c, + R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 0x21d, + R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 0x21e, + R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 0x21f, + + R_AARCH64_TLSLE_MOVW_TPREL_G2 = 0x220, + R_AARCH64_TLSLE_MOVW_TPREL_G1 = 0x221, + R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 0x222, + R_AARCH64_TLSLE_MOVW_TPREL_G0 = 0x223, + R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 0x224, + R_AARCH64_TLSLE_ADD_TPREL_HI12 = 0x225, + R_AARCH64_TLSLE_ADD_TPREL_LO12 = 0x226, + R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 0x227, + R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 0x228, + R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 0x229, + R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 0x22a, + R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 0x22b, + R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 0x22c, + R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 0x22d, + R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 0x22e, + R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 0x22f, + + R_AARCH64_TLSDESC_ADR_PAGE = 0x232, + R_AARCH64_TLSDESC_LD64_LO12_NC = 0x233, + R_AARCH64_TLSDESC_ADD_LO12_NC = 0x234, + + R_AARCH64_TLSDESC_CALL = 0x239 +}; + +// ARM Specific e_flags +enum : u32 +{ + EF_ARM_SOFT_FLOAT = 0x00000200U, + EF_ARM_VFP_FLOAT = 0x00000400U, + EF_ARM_EABI_UNKNOWN = 0x00000000U, + EF_ARM_EABI_VER1 = 0x01000000U, + EF_ARM_EABI_VER2 = 0x02000000U, + EF_ARM_EABI_VER3 = 0x03000000U, + EF_ARM_EABI_VER4 = 0x04000000U, + EF_ARM_EABI_VER5 = 0x05000000U, + EF_ARM_EABIMASK = 0xFF000000U +}; + +// ELF Relocation types for ARM +// Meets 2.08 ABI Specs. + +enum +{ + R_ARM_NONE = 0x00, + R_ARM_PC24 = 0x01, + R_ARM_ABS32 = 0x02, + R_ARM_REL32 = 0x03, + R_ARM_LDR_PC_G0 = 0x04, + R_ARM_ABS16 = 0x05, + R_ARM_ABS12 = 0x06, + R_ARM_THM_ABS5 = 0x07, + R_ARM_ABS8 = 0x08, + R_ARM_SBREL32 = 0x09, + R_ARM_THM_CALL = 0x0a, + R_ARM_THM_PC8 = 0x0b, + R_ARM_BREL_ADJ = 0x0c, + R_ARM_TLS_DESC = 0x0d, + R_ARM_THM_SWI8 = 0x0e, + R_ARM_XPC25 = 0x0f, + R_ARM_THM_XPC22 = 0x10, + R_ARM_TLS_DTPMOD32 = 0x11, + R_ARM_TLS_DTPOFF32 = 0x12, + R_ARM_TLS_TPOFF32 = 0x13, + R_ARM_COPY = 0x14, + R_ARM_GLOB_DAT = 0x15, + R_ARM_JUMP_SLOT = 0x16, + R_ARM_RELATIVE = 0x17, + R_ARM_GOTOFF32 = 0x18, + R_ARM_BASE_PREL = 0x19, + R_ARM_GOT_BREL = 0x1a, + R_ARM_PLT32 = 0x1b, + R_ARM_CALL = 0x1c, + R_ARM_JUMP24 = 0x1d, + R_ARM_THM_JUMP24 = 0x1e, + R_ARM_BASE_ABS = 0x1f, + R_ARM_ALU_PCREL_7_0 = 0x20, + R_ARM_ALU_PCREL_15_8 = 0x21, + R_ARM_ALU_PCREL_23_15 = 0x22, + R_ARM_LDR_SBREL_11_0_NC = 0x23, + R_ARM_ALU_SBREL_19_12_NC = 0x24, + R_ARM_ALU_SBREL_27_20_CK = 0x25, + R_ARM_TARGET1 = 0x26, + R_ARM_SBREL31 = 0x27, + R_ARM_V4BX = 0x28, + R_ARM_TARGET2 = 0x29, + R_ARM_PREL31 = 0x2a, + R_ARM_MOVW_ABS_NC = 0x2b, + R_ARM_MOVT_ABS = 0x2c, + R_ARM_MOVW_PREL_NC = 0x2d, + R_ARM_MOVT_PREL = 0x2e, + R_ARM_THM_MOVW_ABS_NC = 0x2f, + R_ARM_THM_MOVT_ABS = 0x30, + R_ARM_THM_MOVW_PREL_NC = 0x31, + R_ARM_THM_MOVT_PREL = 0x32, + R_ARM_THM_JUMP19 = 0x33, + R_ARM_THM_JUMP6 = 0x34, + R_ARM_THM_ALU_PREL_11_0 = 0x35, + R_ARM_THM_PC12 = 0x36, + R_ARM_ABS32_NOI = 0x37, + R_ARM_REL32_NOI = 0x38, + R_ARM_ALU_PC_G0_NC = 0x39, + R_ARM_ALU_PC_G0 = 0x3a, + R_ARM_ALU_PC_G1_NC = 0x3b, + R_ARM_ALU_PC_G1 = 0x3c, + R_ARM_ALU_PC_G2 = 0x3d, + R_ARM_LDR_PC_G1 = 0x3e, + R_ARM_LDR_PC_G2 = 0x3f, + R_ARM_LDRS_PC_G0 = 0x40, + R_ARM_LDRS_PC_G1 = 0x41, + R_ARM_LDRS_PC_G2 = 0x42, + R_ARM_LDC_PC_G0 = 0x43, + R_ARM_LDC_PC_G1 = 0x44, + R_ARM_LDC_PC_G2 = 0x45, + R_ARM_ALU_SB_G0_NC = 0x46, + R_ARM_ALU_SB_G0 = 0x47, + R_ARM_ALU_SB_G1_NC = 0x48, + R_ARM_ALU_SB_G1 = 0x49, + R_ARM_ALU_SB_G2 = 0x4a, + R_ARM_LDR_SB_G0 = 0x4b, + R_ARM_LDR_SB_G1 = 0x4c, + R_ARM_LDR_SB_G2 = 0x4d, + R_ARM_LDRS_SB_G0 = 0x4e, + R_ARM_LDRS_SB_G1 = 0x4f, + R_ARM_LDRS_SB_G2 = 0x50, + R_ARM_LDC_SB_G0 = 0x51, + R_ARM_LDC_SB_G1 = 0x52, + R_ARM_LDC_SB_G2 = 0x53, + R_ARM_MOVW_BREL_NC = 0x54, + R_ARM_MOVT_BREL = 0x55, + R_ARM_MOVW_BREL = 0x56, + R_ARM_THM_MOVW_BREL_NC = 0x57, + R_ARM_THM_MOVT_BREL = 0x58, + R_ARM_THM_MOVW_BREL = 0x59, + R_ARM_TLS_GOTDESC = 0x5a, + R_ARM_TLS_CALL = 0x5b, + R_ARM_TLS_DESCSEQ = 0x5c, + R_ARM_THM_TLS_CALL = 0x5d, + R_ARM_PLT32_ABS = 0x5e, + R_ARM_GOT_ABS = 0x5f, + R_ARM_GOT_PREL = 0x60, + R_ARM_GOT_BREL12 = 0x61, + R_ARM_GOTOFF12 = 0x62, + R_ARM_GOTRELAX = 0x63, + R_ARM_GNU_VTENTRY = 0x64, + R_ARM_GNU_VTINHERIT = 0x65, + R_ARM_THM_JUMP11 = 0x66, + R_ARM_THM_JUMP8 = 0x67, + R_ARM_TLS_GD32 = 0x68, + R_ARM_TLS_LDM32 = 0x69, + R_ARM_TLS_LDO32 = 0x6a, + R_ARM_TLS_IE32 = 0x6b, + R_ARM_TLS_LE32 = 0x6c, + R_ARM_TLS_LDO12 = 0x6d, + R_ARM_TLS_LE12 = 0x6e, + R_ARM_TLS_IE12GP = 0x6f, + R_ARM_PRIVATE_0 = 0x70, + R_ARM_PRIVATE_1 = 0x71, + R_ARM_PRIVATE_2 = 0x72, + R_ARM_PRIVATE_3 = 0x73, + R_ARM_PRIVATE_4 = 0x74, + R_ARM_PRIVATE_5 = 0x75, + R_ARM_PRIVATE_6 = 0x76, + R_ARM_PRIVATE_7 = 0x77, + R_ARM_PRIVATE_8 = 0x78, + R_ARM_PRIVATE_9 = 0x79, + R_ARM_PRIVATE_10 = 0x7a, + R_ARM_PRIVATE_11 = 0x7b, + R_ARM_PRIVATE_12 = 0x7c, + R_ARM_PRIVATE_13 = 0x7d, + R_ARM_PRIVATE_14 = 0x7e, + R_ARM_PRIVATE_15 = 0x7f, + R_ARM_ME_TOO = 0x80, + R_ARM_THM_TLS_DESCSEQ16 = 0x81, + R_ARM_THM_TLS_DESCSEQ32 = 0x82 +}; + +// Mips Specific e_flags +enum : u32 +{ + EF_MIPS_NOREORDER = 0x00000001, // Don't reorder instructions + EF_MIPS_PIC = 0x00000002, // Position independent code + EF_MIPS_CPIC = 0x00000004, // Call object with Position independent code + EF_MIPS_ABI_O32 = 0x00001000, // This file follows the first MIPS 32 bit ABI + + //ARCH_ASE + EF_MIPS_MICROMIPS = 0x02000000, // microMIPS + EF_MIPS_ARCH_ASE_M16 = + 0x04000000, // Has Mips-16 ISA extensions + //ARCH + EF_MIPS_ARCH_1 = 0x00000000, // MIPS1 instruction set + EF_MIPS_ARCH_2 = 0x10000000, // MIPS2 instruction set + EF_MIPS_ARCH_3 = 0x20000000, // MIPS3 instruction set + EF_MIPS_ARCH_4 = 0x30000000, // MIPS4 instruction set + EF_MIPS_ARCH_5 = 0x40000000, // MIPS5 instruction set + EF_MIPS_ARCH_32 = 0x50000000, // MIPS32 instruction set per linux not elf.h + EF_MIPS_ARCH_64 = 0x60000000, // MIPS64 instruction set per linux not elf.h + EF_MIPS_ARCH_32R2 = 0x70000000, // mips32r2 + EF_MIPS_ARCH_64R2 = 0x80000000, // mips64r2 + EF_MIPS_ARCH = 0xf0000000 // Mask for applying EF_MIPS_ARCH_ variant +}; + +// ELF Relocation types for Mips +// . +enum +{ + R_MIPS_NONE = 0, + R_MIPS_16 = 1, + R_MIPS_32 = 2, + R_MIPS_REL32 = 3, + R_MIPS_26 = 4, + R_MIPS_HI16 = 5, + R_MIPS_LO16 = 6, + R_MIPS_GPREL16 = 7, + R_MIPS_LITERAL = 8, + R_MIPS_GOT16 = 9, + R_MIPS_GOT = 9, + R_MIPS_PC16 = 10, + R_MIPS_CALL16 = 11, + R_MIPS_GPREL32 = 12, + R_MIPS_UNUSED1 = 13, + R_MIPS_UNUSED2 = 14, + R_MIPS_SHIFT5 = 16, + R_MIPS_SHIFT6 = 17, + R_MIPS_64 = 18, + R_MIPS_GOT_DISP = 19, + R_MIPS_GOT_PAGE = 20, + R_MIPS_GOT_OFST = 21, + R_MIPS_GOT_HI16 = 22, + R_MIPS_GOT_LO16 = 23, + R_MIPS_SUB = 24, + R_MIPS_INSERT_A = 25, + R_MIPS_INSERT_B = 26, + R_MIPS_DELETE = 27, + R_MIPS_HIGHER = 28, + R_MIPS_HIGHEST = 29, + R_MIPS_CALL_HI16 = 30, + R_MIPS_CALL_LO16 = 31, + R_MIPS_SCN_DISP = 32, + R_MIPS_REL16 = 33, + R_MIPS_ADD_IMMEDIATE = 34, + R_MIPS_PJUMP = 35, + R_MIPS_RELGOT = 36, + R_MIPS_JALR = 37, + R_MIPS_TLS_DTPMOD32 = 38, + R_MIPS_TLS_DTPREL32 = 39, + R_MIPS_TLS_DTPMOD64 = 40, + R_MIPS_TLS_DTPREL64 = 41, + R_MIPS_TLS_GD = 42, + R_MIPS_TLS_LDM = 43, + R_MIPS_TLS_DTPREL_HI16 = 44, + R_MIPS_TLS_DTPREL_LO16 = 45, + R_MIPS_TLS_GOTTPREL = 46, + R_MIPS_TLS_TPREL32 = 47, + R_MIPS_TLS_TPREL64 = 48, + R_MIPS_TLS_TPREL_HI16 = 49, + R_MIPS_TLS_TPREL_LO16 = 50, + R_MIPS_GLOB_DAT = 51, + R_MIPS_COPY = 126, + R_MIPS_JUMP_SLOT = 127, + R_MIPS_NUM = 218 +}; + +// Special values for the st_other field in the symbol table entry for MIPS. +enum +{ + STO_MIPS_MICROMIPS = 0x80 // MIPS Specific ISA for MicroMips +}; + +// Hexagon Specific e_flags +// Release 5 ABI +enum +{ + // Object processor version flags, bits[3:0] + EF_HEXAGON_MACH_V2 = 0x00000001, // Hexagon V2 + EF_HEXAGON_MACH_V3 = 0x00000002, // Hexagon V3 + EF_HEXAGON_MACH_V4 = 0x00000003, // Hexagon V4 + EF_HEXAGON_MACH_V5 = 0x00000004, // Hexagon V5 + + // Highest ISA version flags + EF_HEXAGON_ISA_MACH = 0x00000000, // Same as specified in bits[3:0] + // of e_flags + EF_HEXAGON_ISA_V2 = 0x00000010, // Hexagon V2 ISA + EF_HEXAGON_ISA_V3 = 0x00000020, // Hexagon V3 ISA + EF_HEXAGON_ISA_V4 = 0x00000030, // Hexagon V4 ISA + EF_HEXAGON_ISA_V5 = 0x00000040 // Hexagon V5 ISA +}; + +// Hexagon specific Section indexes for common small data +// Release 5 ABI +enum +{ + SHN_HEXAGON_SCOMMON = 0xff00, // Other access sizes + SHN_HEXAGON_SCOMMON_1 = 0xff01, // Byte-sized access + SHN_HEXAGON_SCOMMON_2 = 0xff02, // Half-word-sized access + SHN_HEXAGON_SCOMMON_4 = 0xff03, // Word-sized access + SHN_HEXAGON_SCOMMON_8 = 0xff04 // Double-word-size access +}; + +// ELF Relocation types for Hexagon +// Release 5 ABI +enum +{ + R_HEX_NONE = 0, + R_HEX_B22_PCREL = 1, + R_HEX_B15_PCREL = 2, + R_HEX_B7_PCREL = 3, + R_HEX_LO16 = 4, + R_HEX_HI16 = 5, + R_HEX_32 = 6, + R_HEX_16 = 7, + R_HEX_8 = 8, + R_HEX_GPREL16_0 = 9, + R_HEX_GPREL16_1 = 10, + R_HEX_GPREL16_2 = 11, + R_HEX_GPREL16_3 = 12, + R_HEX_HL16 = 13, + R_HEX_B13_PCREL = 14, + R_HEX_B9_PCREL = 15, + R_HEX_B32_PCREL_X = 16, + R_HEX_32_6_X = 17, + R_HEX_B22_PCREL_X = 18, + R_HEX_B15_PCREL_X = 19, + R_HEX_B13_PCREL_X = 20, + R_HEX_B9_PCREL_X = 21, + R_HEX_B7_PCREL_X = 22, + R_HEX_16_X = 23, + R_HEX_12_X = 24, + R_HEX_11_X = 25, + R_HEX_10_X = 26, + R_HEX_9_X = 27, + R_HEX_8_X = 28, + R_HEX_7_X = 29, + R_HEX_6_X = 30, + R_HEX_32_PCREL = 31, + R_HEX_COPY = 32, + R_HEX_GLOB_DAT = 33, + R_HEX_JMP_SLOT = 34, + R_HEX_RELATIVE = 35, + R_HEX_PLT_B22_PCREL = 36, + R_HEX_GOTREL_LO16 = 37, + R_HEX_GOTREL_HI16 = 38, + R_HEX_GOTREL_32 = 39, + R_HEX_GOT_LO16 = 40, + R_HEX_GOT_HI16 = 41, + R_HEX_GOT_32 = 42, + R_HEX_GOT_16 = 43, + R_HEX_DTPMOD_32 = 44, + R_HEX_DTPREL_LO16 = 45, + R_HEX_DTPREL_HI16 = 46, + R_HEX_DTPREL_32 = 47, + R_HEX_DTPREL_16 = 48, + R_HEX_GD_PLT_B22_PCREL = 49, + R_HEX_GD_GOT_LO16 = 50, + R_HEX_GD_GOT_HI16 = 51, + R_HEX_GD_GOT_32 = 52, + R_HEX_GD_GOT_16 = 53, + R_HEX_IE_LO16 = 54, + R_HEX_IE_HI16 = 55, + R_HEX_IE_32 = 56, + R_HEX_IE_GOT_LO16 = 57, + R_HEX_IE_GOT_HI16 = 58, + R_HEX_IE_GOT_32 = 59, + R_HEX_IE_GOT_16 = 60, + R_HEX_TPREL_LO16 = 61, + R_HEX_TPREL_HI16 = 62, + R_HEX_TPREL_32 = 63, + R_HEX_TPREL_16 = 64, + R_HEX_6_PCREL_X = 65, + R_HEX_GOTREL_32_6_X = 66, + R_HEX_GOTREL_16_X = 67, + R_HEX_GOTREL_11_X = 68, + R_HEX_GOT_32_6_X = 69, + R_HEX_GOT_16_X = 70, + R_HEX_GOT_11_X = 71, + R_HEX_DTPREL_32_6_X = 72, + R_HEX_DTPREL_16_X = 73, + R_HEX_DTPREL_11_X = 74, + R_HEX_GD_GOT_32_6_X = 75, + R_HEX_GD_GOT_16_X = 76, + R_HEX_GD_GOT_11_X = 77, + R_HEX_IE_32_6_X = 78, + R_HEX_IE_16_X = 79, + R_HEX_IE_GOT_32_6_X = 80, + R_HEX_IE_GOT_16_X = 81, + R_HEX_IE_GOT_11_X = 82, + R_HEX_TPREL_32_6_X = 83, + R_HEX_TPREL_16_X = 84, + R_HEX_TPREL_11_X = 85 +}; + +// ELF Relocation types for S390/zSeries +enum +{ + R_390_NONE = 0, + R_390_8 = 1, + R_390_12 = 2, + R_390_16 = 3, + R_390_32 = 4, + R_390_PC32 = 5, + R_390_GOT12 = 6, + R_390_GOT32 = 7, + R_390_PLT32 = 8, + R_390_COPY = 9, + R_390_GLOB_DAT = 10, + R_390_JMP_SLOT = 11, + R_390_RELATIVE = 12, + R_390_GOTOFF = 13, + R_390_GOTPC = 14, + R_390_GOT16 = 15, + R_390_PC16 = 16, + R_390_PC16DBL = 17, + R_390_PLT16DBL = 18, + R_390_PC32DBL = 19, + R_390_PLT32DBL = 20, + R_390_GOTPCDBL = 21, + R_390_64 = 22, + R_390_PC64 = 23, + R_390_GOT64 = 24, + R_390_PLT64 = 25, + R_390_GOTENT = 26, + R_390_GOTOFF16 = 27, + R_390_GOTOFF64 = 28, + R_390_GOTPLT12 = 29, + R_390_GOTPLT16 = 30, + R_390_GOTPLT32 = 31, + R_390_GOTPLT64 = 32, + R_390_GOTPLTENT = 33, + R_390_PLTOFF16 = 34, + R_390_PLTOFF32 = 35, + R_390_PLTOFF64 = 36, + R_390_TLS_LOAD = 37, + R_390_TLS_GDCALL = 38, + R_390_TLS_LDCALL = 39, + R_390_TLS_GD32 = 40, + R_390_TLS_GD64 = 41, + R_390_TLS_GOTIE12 = 42, + R_390_TLS_GOTIE32 = 43, + R_390_TLS_GOTIE64 = 44, + R_390_TLS_LDM32 = 45, + R_390_TLS_LDM64 = 46, + R_390_TLS_IE32 = 47, + R_390_TLS_IE64 = 48, + R_390_TLS_IEENT = 49, + R_390_TLS_LE32 = 50, + R_390_TLS_LE64 = 51, + R_390_TLS_LDO32 = 52, + R_390_TLS_LDO64 = 53, + R_390_TLS_DTPMOD = 54, + R_390_TLS_DTPOFF = 55, + R_390_TLS_TPOFF = 56, + R_390_20 = 57, + R_390_GOT20 = 58, + R_390_GOTPLT20 = 59, + R_390_TLS_GOTIE20 = 60, + R_390_IRELATIVE = 61 +}; + +// Section header. +struct Elf32_Shdr +{ + Elf32_Word sh_name; // Section name (index into string table) + Elf32_Word sh_type; // Section type (SHT_*) + Elf32_Word sh_flags; // Section flags (SHF_*) + Elf32_Addr sh_addr; // Address where section is to be loaded + Elf32_Off sh_offset; // File offset of section data, in bytes + Elf32_Word sh_size; // Size of section, in bytes + Elf32_Word sh_link; // Section type-specific header table index link + Elf32_Word sh_info; // Section type-specific extra information + Elf32_Word sh_addralign; // Section address alignment + Elf32_Word sh_entsize; // Size of records contained within the section +}; + +// Section header for ELF64 - same fields as ELF32, different types. +struct Elf64_Shdr +{ + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +}; + +// Special section indices. +enum +{ + SHN_UNDEF = 0, // Undefined, missing, irrelevant, or meaningless + SHN_LORESERVE = 0xff00, // Lowest reserved index + SHN_LOPROC = 0xff00, // Lowest processor-specific index + SHN_HIPROC = 0xff1f, // Highest processor-specific index + SHN_LOOS = 0xff20, // Lowest operating system-specific index + SHN_HIOS = 0xff3f, // Highest operating system-specific index + SHN_ABS = 0xfff1, // Symbol has absolute value; does not need relocation + SHN_COMMON = 0xfff2, // FORTRAN COMMON or C external global variables + SHN_XINDEX = 0xffff, // Mark that the index is >= SHN_LORESERVE + SHN_HIRESERVE = 0xffff // Highest reserved index +}; + +// Section types. +enum : u32 +{ + SHT_NULL = 0, // No associated section (inactive entry). + SHT_PROGBITS = 1, // Program-defined contents. + SHT_SYMTAB = 2, // Symbol table. + SHT_STRTAB = 3, // String table. + SHT_RELA = 4, // Relocation entries; explicit addends. + SHT_HASH = 5, // Symbol hash table. + SHT_DYNAMIC = 6, // Information for dynamic linking. + SHT_NOTE = 7, // Information about the file. + SHT_NOBITS = 8, // Data occupies no space in the file. + SHT_REL = 9, // Relocation entries; no explicit addends. + SHT_SHLIB = 10, // Reserved. + SHT_DYNSYM = 11, // Symbol table. + SHT_INIT_ARRAY = 14, // Pointers to initialization functions. + SHT_FINI_ARRAY = 15, // Pointers to termination functions. + SHT_PREINIT_ARRAY = 16, // Pointers to pre-init functions. + SHT_GROUP = 17, // Section group. + SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries. + SHT_LOOS = 0x60000000, // Lowest operating system-specific type. + SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes. + SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table. + SHT_GNU_verdef = 0x6ffffffd, // GNU version definitions. + SHT_GNU_verneed = 0x6ffffffe, // GNU version references. + SHT_GNU_versym = 0x6fffffff, // GNU symbol versions table. + SHT_HIOS = 0x6fffffff, // Highest operating system-specific type. + SHT_LOPROC = 0x70000000, // Lowest processor arch-specific type. + // Fixme: All this is duplicated in MCSectionELF. Why?? + // Exception Index table + SHT_ARM_EXIDX = 0x70000001U, + // BPABI DLL dynamic linking pre-emption map + SHT_ARM_PREEMPTMAP = 0x70000002U, + // Object file compatibility attributes + SHT_ARM_ATTRIBUTES = 0x70000003U, + SHT_ARM_DEBUGOVERLAY = 0x70000004U, + SHT_ARM_OVERLAYSECTION = 0x70000005U, + SHT_HEX_ORDERED = 0x70000000, // Link editor is to sort the entries in + // this section based on their sizes + SHT_X86_64_UNWIND = 0x70000001, // Unwind information + + SHT_MIPS_REGINFO = 0x70000006, // Register usage information + SHT_MIPS_OPTIONS = 0x7000000d, // General options + + SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. + SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. + SHT_HIUSER = 0xffffffff // Highest type reserved for applications. +}; + +// Section flags. +enum : u32 +{ + // Section data should be writable during execution. + SHF_WRITE = 0x1, + + // Section occupies memory during program execution. + SHF_ALLOC = 0x2, + + // Section contains executable machine instructions. + SHF_EXECINSTR = 0x4, + + // The data in this section may be merged. + SHF_MERGE = 0x10, + + // The data in this section is null-terminated strings. + SHF_STRINGS = 0x20, + + // A field in this section holds a section header table index. + SHF_INFO_LINK = 0x40U, + + // Adds special ordering requirements for link editors. + SHF_LINK_ORDER = 0x80U, + + // This section requires special OS-specific processing to avoid incorrect + // behavior. + SHF_OS_NONCONFORMING = 0x100U, + + // This section is a member of a section group. + SHF_GROUP = 0x200U, + + // This section holds Thread-Local Storage. + SHF_TLS = 0x400U, + + // This section is excluded from the final executable or shared library. + SHF_EXCLUDE = 0x80000000U, + + // Start of target-specific flags. + + /// XCORE_SHF_CP_SECTION - All sections with the "c" flag are grouped + /// together by the linker to form the constant pool and the cp register is + /// set to the start of the constant pool by the boot code. + XCORE_SHF_CP_SECTION = 0x800U, + + /// XCORE_SHF_DP_SECTION - All sections with the "d" flag are grouped + /// together by the linker to form the data section and the dp register is + /// set to the start of the section by the boot code. + XCORE_SHF_DP_SECTION = 0x1000U, + + SHF_MASKOS = 0x0ff00000, + + // Bits indicating processor-specific flags. + SHF_MASKPROC = 0xf0000000, + + // If an object file section does not have this flag set, then it may not hold + // more than 2GB and can be freely referred to in objects using smaller code + // models. Otherwise, only objects using larger code models can refer to them. + // For example, a medium code model object can refer to data in a section that + // sets this flag besides being able to refer to data in a section that does + // not set it; likewise, a small code model object can refer only to code in a + // section that does not set this flag. + SHF_X86_64_LARGE = 0x10000000, + + // All sections with the GPREL flag are grouped into a global data area + // for faster accesses + SHF_HEX_GPREL = 0x10000000, + + // Do not strip this section. FIXME: We need target specific SHF_ enums. + SHF_MIPS_NOSTRIP = 0x8000000 +}; + +// Section Group Flags +enum : u32 +{ + GRP_COMDAT = 0x1, + GRP_MASKOS = 0x0ff00000, + GRP_MASKPROC = 0xf0000000 +}; + +// Symbol table entries for ELF32. +struct Elf32_Sym +{ + Elf32_Word st_name; // Symbol name (index into string table) + Elf32_Addr st_value; // Value or address associated with the symbol + Elf32_Word st_size; // Size of the symbol + unsigned char st_info; // Symbol's type and binding attributes + unsigned char st_other; // Must be zero; reserved + Elf32_Half st_shndx; // Which section (header table index) it's defined in +}; + +// Symbol table entries for ELF64. +struct Elf64_Sym +{ + Elf64_Word st_name; // Symbol name (index into string table) + unsigned char st_info; // Symbol's type and binding attributes + unsigned char st_other; // Must be zero; reserved + Elf64_Half st_shndx; // Which section (header tbl index) it's defined in + Elf64_Addr st_value; // Value or address associated with the symbol + Elf64_Xword st_size; // Size of the symbol +}; + +// The size (in bytes) of symbol table entries. +enum +{ + SYMENTRY_SIZE32 = 16, // 32-bit symbol entry size + SYMENTRY_SIZE64 = 24 // 64-bit symbol entry size. +}; + +// Symbol bindings. +enum +{ + STB_LOCAL = 0, // Local symbol, not visible outside obj file containing def + STB_GLOBAL = 1, // Global symbol, visible to all object files being combined + STB_WEAK = 2, // Weak symbol, like global but lower-precedence + STB_LOOS = 10, // Lowest operating system-specific binding type + STB_HIOS = 12, // Highest operating system-specific binding type + STB_LOPROC = 13, // Lowest processor-specific binding type + STB_HIPROC = 15 // Highest processor-specific binding type +}; + +// Symbol types. +enum +{ + STT_NOTYPE = 0, // Symbol's type is not specified + STT_OBJECT = 1, // Symbol is a data object (variable, array, etc.) + STT_FUNC = 2, // Symbol is executable code (function, etc.) + STT_SECTION = 3, // Symbol refers to a section + STT_FILE = 4, // Local, absolute symbol that refers to a file + STT_COMMON = 5, // An uninitialized common block + STT_TLS = 6, // Thread local data object + STT_LOOS = 7, // Lowest operating system-specific symbol type + STT_HIOS = 8, // Highest operating system-specific symbol type + STT_GNU_IFUNC = 10, // GNU indirect function + STT_LOPROC = 13, // Lowest processor-specific symbol type + STT_HIPROC = 15 // Highest processor-specific symbol type +}; + +enum +{ + STV_DEFAULT = 0, // Visibility is specified by binding type + STV_INTERNAL = 1, // Defined by processor supplements + STV_HIDDEN = 2, // Not visible to other components + STV_PROTECTED = 3 // Visible in other components but not preemptable +}; + +// Symbol number. +enum +{ + STN_UNDEF = 0 +}; + +// Relocation entry, without explicit addend. +struct Elf32_Rel +{ + Elf32_Addr r_offset; // Location (file byte offset, or program virtual addr) + Elf32_Word r_info; // Symbol table index and type of relocation to apply +}; + +// Relocation entry with explicit addend. +struct Elf32_Rela +{ + Elf32_Addr r_offset; // Location (file byte offset, or program virtual addr) + Elf32_Word r_info; // Symbol table index and type of relocation to apply + Elf32_Sword r_addend; // Compute value for relocatable field by adding this +}; + +// Relocation entry, without explicit addend. +struct Elf64_Rel +{ + Elf64_Addr r_offset; // Location (file byte offset, or program virtual addr). + Elf64_Xword r_info; // Symbol table index and type of relocation to apply. +}; + +// Relocation entry with explicit addend. +struct Elf64_Rela +{ + Elf64_Addr r_offset; // Location (file byte offset, or program virtual addr). + Elf64_Xword r_info; // Symbol table index and type of relocation to apply. + Elf64_Sxword r_addend; // Compute value for relocatable field by adding this. +}; + +// Program header for ELF32. +struct Elf32_Phdr +{ + Elf32_Word p_type; // Type of segment + Elf32_Off p_offset; // File offset where segment is located, in bytes + Elf32_Addr p_vaddr; // Virtual address of beginning of segment + Elf32_Addr p_paddr; // Physical address of beginning of segment (OS-specific) + Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero) + Elf32_Word p_memsz; // Num. of bytes in mem image of segment (may be zero) + Elf32_Word p_flags; // Segment flags + Elf32_Word p_align; // Segment alignment constraint +}; + +// Program header for ELF64. +struct Elf64_Phdr +{ + Elf64_Word p_type; // Type of segment + Elf64_Word p_flags; // Segment flags + Elf64_Off p_offset; // File offset where segment is located, in bytes + Elf64_Addr p_vaddr; // Virtual address of beginning of segment + Elf64_Addr p_paddr; // Physical addr of beginning of segment (OS-specific) + Elf64_Xword p_filesz; // Num. of bytes in file image of segment (may be zero) + Elf64_Xword p_memsz; // Num. of bytes in mem image of segment (may be zero) + Elf64_Xword p_align; // Segment alignment constraint +}; + +// Segment types. +enum +{ + PT_NULL = 0, // Unused segment. + PT_LOAD = 1, // Loadable segment. + PT_DYNAMIC = 2, // Dynamic linking information. + PT_INTERP = 3, // Interpreter pathname. + PT_NOTE = 4, // Auxiliary information. + PT_SHLIB = 5, // Reserved. + PT_PHDR = 6, // The program header table itself. + PT_TLS = 7, // The thread-local storage template. + PT_LOOS = 0x60000000, // Lowest operating system-specific pt entry type. + PT_HIOS = 0x6fffffff, // Highest operating system-specific pt entry type. + PT_LOPROC = 0x70000000, // Lowest processor-specific program hdr entry type. + PT_HIPROC = 0x7fffffff, // Highest processor-specific program hdr entry type. + + // x86-64 program header types. + // These all contain stack unwind tables. + PT_GNU_EH_FRAME = 0x6474e550, + PT_SUNW_EH_FRAME = 0x6474e550, + PT_SUNW_UNWIND = 0x6464e550, + + PT_GNU_STACK = 0x6474e551, // Indicates stack executability. + PT_GNU_RELRO = 0x6474e552, // Read-only after relocation. + + // ARM program header types. + PT_ARM_ARCHEXT = 0x70000000, // Platform architecture compatibility info + // These all contain stack unwind tables. + PT_ARM_EXIDX = 0x70000001, + PT_ARM_UNWIND = 0x70000001 +}; + +// Segment flag bits. +enum : u32 +{ + PF_X = 1, // Execute + PF_W = 2, // Write + PF_R = 4, // Read + PF_MASKOS = 0x0ff00000, // Bits for operating system-specific semantics. + PF_MASKPROC = 0xf0000000 // Bits for processor-specific semantics. +}; + +// Dynamic table entry for ELF32. +struct Elf32_Dyn +{ + Elf32_Sword d_tag; // Type of dynamic table entry. + union + { + Elf32_Word d_val; // Integer value of entry. + Elf32_Addr d_ptr; // Pointer value of entry. + } d_un; +}; + +// Dynamic table entry for ELF64. +struct Elf64_Dyn +{ + Elf64_Sxword d_tag; // Type of dynamic table entry. + union + { + Elf64_Xword d_val; // Integer value of entry. + Elf64_Addr d_ptr; // Pointer value of entry. + } d_un; +}; + +// Dynamic table entry tags. +enum +{ + DT_NULL = 0, // Marks end of dynamic array. + DT_NEEDED = 1, // String table offset of needed library. + DT_PLTRELSZ = 2, // Size of relocation entries in PLT. + DT_PLTGOT = 3, // Address associated with linkage table. + DT_HASH = 4, // Address of symbolic hash table. + DT_STRTAB = 5, // Address of dynamic string table. + DT_SYMTAB = 6, // Address of dynamic symbol table. + DT_RELA = 7, // Address of relocation table (Rela entries). + DT_RELASZ = 8, // Size of Rela relocation table. + DT_RELAENT = 9, // Size of a Rela relocation entry. + DT_STRSZ = 10, // Total size of the string table. + DT_SYMENT = 11, // Size of a symbol table entry. + DT_INIT = 12, // Address of initialization function. + DT_FINI = 13, // Address of termination function. + DT_SONAME = 14, // String table offset of a shared objects name. + DT_RPATH = 15, // String table offset of library search path. + DT_SYMBOLIC = 16, // Changes symbol resolution algorithm. + DT_REL = 17, // Address of relocation table (Rel entries). + DT_RELSZ = 18, // Size of Rel relocation table. + DT_RELENT = 19, // Size of a Rel relocation entry. + DT_PLTREL = 20, // Type of relocation entry used for linking. + DT_DEBUG = 21, // Reserved for debugger. + DT_TEXTREL = 22, // Relocations exist for non-writable segments. + DT_JMPREL = 23, // Address of relocations associated with PLT. + DT_BIND_NOW = 24, // Process all relocations before execution. + DT_INIT_ARRAY = 25, // Pointer to array of initialization functions. + DT_FINI_ARRAY = 26, // Pointer to array of termination functions. + DT_INIT_ARRAYSZ = 27, // Size of DT_INIT_ARRAY. + DT_FINI_ARRAYSZ = 28, // Size of DT_FINI_ARRAY. + DT_RUNPATH = 29, // String table offset of lib search path. + DT_FLAGS = 30, // Flags. + DT_ENCODING = 32, // Values from here to DT_LOOS follow the rules + // for the interpretation of the d_un union. + + DT_PREINIT_ARRAY = 32, // Pointer to array of preinit functions. + DT_PREINIT_ARRAYSZ = 33, // Size of the DT_PREINIT_ARRAY array. + + DT_LOOS = 0x60000000, // Start of environment specific tags. + DT_HIOS = 0x6FFFFFFF, // End of environment specific tags. + DT_LOPROC = 0x70000000, // Start of processor specific tags. + DT_HIPROC = 0x7FFFFFFF, // End of processor specific tags. + + DT_RELACOUNT = 0x6FFFFFF9, // ELF32_Rela count. + DT_RELCOUNT = 0x6FFFFFFA, // ELF32_Rel count. + + DT_FLAGS_1 = 0X6FFFFFFB, // Flags_1. + DT_VERDEF = 0X6FFFFFFC, // The address of the version definition table. + DT_VERDEFNUM = 0X6FFFFFFD, // The number of entries in DT_VERDEF. + DT_VERNEED = 0X6FFFFFFE, // The address of the version Dependency table. + DT_VERNEEDNUM = 0X6FFFFFFF, // The number of entries in DT_VERNEED. + + // Mips specific dynamic table entry tags. + DT_MIPS_RLD_VERSION = 0x70000001, // 32 bit version number for runtime + // linker interface. + DT_MIPS_TIME_STAMP = 0x70000002, // Time stamp. + DT_MIPS_ICHECKSUM = 0x70000003, // Checksum of external strings + // and common sizes. + DT_MIPS_IVERSION = 0x70000004, // Index of version string + // in string table. + DT_MIPS_FLAGS = 0x70000005, // 32 bits of flags. + DT_MIPS_BASE_ADDRESS = 0x70000006, // Base address of the segment. + DT_MIPS_MSYM = 0x70000007, // Address of .msym section. + DT_MIPS_CONFLICT = 0x70000008, // Address of .conflict section. + DT_MIPS_LIBLIST = 0x70000009, // Address of .liblist section. + DT_MIPS_LOCAL_GOTNO = 0x7000000a, // Number of local global offset + // table entries. + DT_MIPS_CONFLICTNO = 0x7000000b, // Number of entries + // in the .conflict section. + DT_MIPS_LIBLISTNO = 0x70000010, // Number of entries + // in the .liblist section. + DT_MIPS_SYMTABNO = 0x70000011, // Number of entries + // in the .dynsym section. + DT_MIPS_UNREFEXTNO = 0x70000012, // Index of first external dynamic symbol + // not referenced locally. + DT_MIPS_GOTSYM = 0x70000013, // Index of first dynamic symbol + // in global offset table. + DT_MIPS_HIPAGENO = 0x70000014, // Number of page table entries + // in global offset table. + DT_MIPS_RLD_MAP = 0x70000016, // Address of run time loader map, + // used for debugging. + DT_MIPS_DELTA_CLASS = 0x70000017, // Delta C++ class definition. + DT_MIPS_DELTA_CLASS_NO = 0x70000018, // Number of entries + // in DT_MIPS_DELTA_CLASS. + DT_MIPS_DELTA_INSTANCE = 0x70000019, // Delta C++ class instances. + DT_MIPS_DELTA_INSTANCE_NO = 0x7000001A, // Number of entries + // in DT_MIPS_DELTA_INSTANCE. + DT_MIPS_DELTA_RELOC = 0x7000001B, // Delta relocations. + DT_MIPS_DELTA_RELOC_NO = 0x7000001C, // Number of entries + // in DT_MIPS_DELTA_RELOC. + DT_MIPS_DELTA_SYM = 0x7000001D, // Delta symbols that Delta + // relocations refer to. + DT_MIPS_DELTA_SYM_NO = 0x7000001E, // Number of entries + // in DT_MIPS_DELTA_SYM. + DT_MIPS_DELTA_CLASSSYM = 0x70000020, // Delta symbols that hold + // class declarations. + DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021, // Number of entries + // in DT_MIPS_DELTA_CLASSSYM. + DT_MIPS_CXX_FLAGS = 0x70000022, // Flags indicating information + // about C++ flavor. + DT_MIPS_PIXIE_INIT = 0x70000023, // Pixie information. + DT_MIPS_SYMBOL_LIB = 0x70000024, // Address of .MIPS.symlib + DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025, // The GOT index of the first PTE + // for a segment + DT_MIPS_LOCAL_GOTIDX = 0x70000026, // The GOT index of the first PTE + // for a local symbol + DT_MIPS_HIDDEN_GOTIDX = 0x70000027, // The GOT index of the first PTE + // for a hidden symbol + DT_MIPS_PROTECTED_GOTIDX = 0x70000028, // The GOT index of the first PTE + // for a protected symbol + DT_MIPS_OPTIONS = 0x70000029, // Address of `.MIPS.options'. + DT_MIPS_INTERFACE = 0x7000002A, // Address of `.interface'. + DT_MIPS_DYNSTR_ALIGN = 0x7000002B, // Unknown. + DT_MIPS_INTERFACE_SIZE = 0x7000002C, // Size of the .interface section. + DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002D, // Size of rld_text_resolve + // function stored in the GOT. + DT_MIPS_PERF_SUFFIX = 0x7000002E, // Default suffix of DSO to be added + // by rld on dlopen() calls. + DT_MIPS_COMPACT_SIZE = 0x7000002F, // Size of compact relocation + // section (O32). + DT_MIPS_GP_VALUE = 0x70000030, // GP value for auxiliary GOTs. + DT_MIPS_AUX_DYNAMIC = 0x70000031, // Address of auxiliary .dynamic. + DT_MIPS_PLTGOT = 0x70000032, // Address of the base of the PLTGOT. + DT_MIPS_RWPLT = 0x70000034 // Points to the base + // of a writable PLT. +}; + +// DT_FLAGS values. +enum +{ + DF_ORIGIN = 0x01, // The object may reference $ORIGIN. + DF_SYMBOLIC = 0x02, // Search the shared lib before searching the exe. + DF_TEXTREL = 0x04, // Relocations may modify a non-writable segment. + DF_BIND_NOW = 0x08, // Process all relocations on load. + DF_STATIC_TLS = 0x10 // Reject attempts to load dynamically. +}; + +// State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 entry. +enum +{ + DF_1_NOW = 0x00000001, // Set RTLD_NOW for this object. + DF_1_GLOBAL = 0x00000002, // Set RTLD_GLOBAL for this object. + DF_1_GROUP = 0x00000004, // Set RTLD_GROUP for this object. + DF_1_NODELETE = 0x00000008, // Set RTLD_NODELETE for this object. + DF_1_LOADFLTR = 0x00000010, // Trigger filtee loading at runtime. + DF_1_INITFIRST = 0x00000020, // Set RTLD_INITFIRST for this object. + DF_1_NOOPEN = 0x00000040, // Set RTLD_NOOPEN for this object. + DF_1_ORIGIN = 0x00000080, // $ORIGIN must be handled. + DF_1_DIRECT = 0x00000100, // Direct binding enabled. + DF_1_TRANS = 0x00000200, + DF_1_INTERPOSE = 0x00000400, // Object is used to interpose. + DF_1_NODEFLIB = 0x00000800, // Ignore default lib search path. + DF_1_NODUMP = 0x00001000, // Object can't be dldump'ed. + DF_1_CONFALT = 0x00002000, // Configuration alternative created. + DF_1_ENDFILTEE = 0x00004000, // Filtee terminates filters search. + DF_1_DISPRELDNE = 0x00008000, // Disp reloc applied at build time. + DF_1_DISPRELPND = 0x00010000 // Disp reloc applied at run-time. +}; + +// DT_MIPS_FLAGS values. +enum +{ + RHF_NONE = 0x00000000, // No flags. + RHF_QUICKSTART = 0x00000001, // Uses shortcut pointers. + RHF_NOTPOT = 0x00000002, // Hash size is not a power of two. + RHS_NO_LIBRARY_REPLACEMENT = 0x00000004, // Ignore LD_LIBRARY_PATH. + RHF_NO_MOVE = 0x00000008, // DSO address may not be relocated. + RHF_SGI_ONLY = 0x00000010, // SGI specific features. + RHF_GUARANTEE_INIT = 0x00000020, // Guarantee that .init will finish + // executing before any non-init + // code in DSO is called. + RHF_DELTA_C_PLUS_PLUS = 0x00000040, // Contains Delta C++ code. + RHF_GUARANTEE_START_INIT = 0x00000080, // Guarantee that .init will start + // executing before any non-init + // code in DSO is called. + RHF_PIXIE = 0x00000100, // Generated by pixie. + RHF_DEFAULT_DELAY_LOAD = 0x00000200, // Delay-load DSO by default. + RHF_REQUICKSTART = 0x00000400, // Object may be requickstarted + RHF_REQUICKSTARTED = 0x00000800, // Object has been requickstarted + RHF_CORD = 0x00001000, // Generated by cord. + RHF_NO_UNRES_UNDEF = 0x00002000, // Object contains no unresolved + // undef symbols. + RHF_RLD_ORDER_SAFE = 0x00004000 // Symbol table is in a safe order. +}; + +// ElfXX_VerDef structure version (GNU versioning) +enum +{ + VER_DEF_NONE = 0, + VER_DEF_CURRENT = 1 +}; + +// VerDef Flags (ElfXX_VerDef::vd_flags) +enum +{ + VER_FLG_BASE = 0x1, + VER_FLG_WEAK = 0x2, + VER_FLG_INFO = 0x4 +}; + +// Special constants for the version table. (SHT_GNU_versym/.gnu.version) +enum +{ + VER_NDX_LOCAL = 0, // Unversioned local symbol + VER_NDX_GLOBAL = 1, // Unversioned global symbol + VERSYM_VERSION = 0x7fff, // Version Index mask + VERSYM_HIDDEN = 0x8000 // Hidden bit (non-default version) +}; + +// ElfXX_VerNeed structure version (GNU versioning) +enum +{ + VER_NEED_NONE = 0, + VER_NEED_CURRENT = 1 +}; + +#endif diff --git a/src/hwinit/btn.c b/src/hwinit/btn.c new file mode 100644 index 0000000..c346fce --- /dev/null +++ b/src/hwinit/btn.c @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "btn.h" +#include "i2c.h" +#include "gpio.h" +#include "t210.h" + +u32 btn_read() +{ + u32 res = 0; + if (!gpio_read(GPIO_PORT_X, GPIO_PIN_7)) + res |= BTN_VOL_DOWN; + if (!gpio_read(GPIO_PORT_X, GPIO_PIN_6)) + res |= BTN_VOL_UP; + if (i2c_recv_byte(4, 0x3C, 0x15) & 0x4) + res |= BTN_POWER; + return res; +} + +u32 btn_wait() +{ + u32 res = 0, btn = btn_read(); + do + { + res = btn_read(); + } while (btn == res); + return res; +} diff --git a/src/hwinit/btn.h b/src/hwinit/btn.h new file mode 100644 index 0000000..8c5a8c5 --- /dev/null +++ b/src/hwinit/btn.h @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _BTN_H_ +#define _BTN_H_ + +#include "types.h" + +#define BTN_POWER 0x1 +#define BTN_VOL_DOWN 0x2 +#define BTN_VOL_UP 0x4 + +u32 btn_read(); +u32 btn_wait(); + +#endif diff --git a/src/hwinit/clock.c b/src/hwinit/clock.c new file mode 100644 index 0000000..efa6ea3 --- /dev/null +++ b/src/hwinit/clock.c @@ -0,0 +1,449 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "clock.h" +#include "t210.h" +#include "util.h" +#include "sdmmc.h" + +static const clock_t _clock_uart[] = { + /* UART A */ { 4, 0x10, 0x178, 6, 0, 0 }, + /* UART B */ { 4, 0x10, 0x17C, 7, 0, 0 }, + /* UART C */ { 8, 0x14, 0x1A0, 0x17, 0, 0 }, + /* UART D */ { 0 }, + /* UART E */ { 0 } +}; + +static const clock_t _clock_i2c[] = { + /* I2C1 */ { 4, 0x10, 0x124, 0xC, 6, 0 }, + /* I2C2 */ { 0 }, + /* I2C3 */ { 0 }, + /* I2C4 */ { 0 }, + /* I2C5 */ { 8, 0x14, 0x128, 0xF, 6, 0 }, + /* I2C6 */ { 0 } +}; + +static clock_t _clock_se = { 0x358, 0x360, 0x42C, 0x1F, 0, 0 }; + +static clock_t _clock_host1x = { 4, 0x10, 0x180, 0x1C, 4, 3 }; +static clock_t _clock_tsec = { 0xC, 0x18, 0x1F4, 0x13, 0, 2 }; +static clock_t _clock_sor_safe = { 0x2A4, 0x298, 0, 0x1E, 0, 0 }; +static clock_t _clock_sor0 = { 0x28C, 0x280, 0, 0x16, 0, 0 }; +static clock_t _clock_sor1 = { 0x28C, 0x280, 0x410, 0x17, 0, 2 }; +static clock_t _clock_kfuse = { 8, 0x14, 0, 8, 0, 0 }; + +static clock_t _clock_cl_dvfs = { 0x35C, 0x364, 0, 0x1B, 0, 0 }; +static clock_t _clock_coresight = { 0xC, 0x18, 0x1D4, 9, 0, 4}; + +void clock_enable(const clock_t *clk) +{ + //Put clock into reset. + CLOCK(clk->reset) = (CLOCK(clk->reset) & ~(1 << clk->index)) | (1 << clk->index); + //Disable. + CLOCK(clk->enable) &= ~(1 << clk->index); + //Configure clock source if required. + if (clk->source) + CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29); + //Enable. + CLOCK(clk->enable) = (CLOCK(clk->enable) & ~(1 << clk->index)) | (1 << clk->index); + //Take clock off reset. + CLOCK(clk->reset) &= ~(1 << clk->index); +} + +void clock_disable(const clock_t *clk) +{ + //Put clock into reset. + CLOCK(clk->reset) = (CLOCK(clk->reset) & ~(1 << clk->index)) | (1 << clk->index); + //Disable. + CLOCK(clk->enable) &= ~(1 << clk->index); +} + +void clock_enable_fuse(u32 enable) +{ + CLOCK(CLK_RST_CONTROLLER_MISC_CLK_ENB) = (CLOCK(CLK_RST_CONTROLLER_MISC_CLK_ENB) & 0xEFFFFFFF) | ((enable & 1) << 28); +} + +void clock_enable_uart(u32 idx) +{ + clock_enable(&_clock_uart[idx]); +} + +void clock_enable_i2c(u32 idx) +{ + clock_enable(&_clock_i2c[idx]); +} + +void clock_enable_se() +{ + clock_enable(&_clock_se); +} + +void clock_enable_host1x() +{ + clock_enable(&_clock_host1x); +} + +void clock_disable_host1x() +{ + clock_disable(&_clock_host1x); +} + +void clock_enable_tsec() +{ + clock_enable(&_clock_tsec); +} + +void clock_disable_tsec() +{ + clock_disable(&_clock_tsec); +} + +void clock_enable_sor_safe() +{ + clock_enable(&_clock_sor_safe); +} + +void clock_disable_sor_safe() +{ + clock_disable(&_clock_sor_safe); +} + +void clock_enable_sor0() +{ + clock_enable(&_clock_sor0); +} + +void clock_disable_sor0() +{ + clock_disable(&_clock_sor0); +} + +void clock_enable_sor1() +{ + clock_enable(&_clock_sor1); +} + +void clock_disable_sor1() +{ + clock_disable(&_clock_sor1); +} + +void clock_enable_kfuse() +{ + //clock_enable(&_clock_kfuse); + CLOCK(0x8) = (CLOCK(0x8) & 0xFFFFFEFF) | 0x100; + CLOCK(0x14) &= 0xFFFFFEFF; + CLOCK(0x14) = (CLOCK(0x14) & 0xFFFFFEFF) | 0x100; + sleep(10); + CLOCK(0x8) &= 0xFFFFFEFF; + sleep(20); +} + +void clock_disable_kfuse() +{ + clock_disable(&_clock_kfuse); +} + +void clock_enable_cl_dvfs() +{ + clock_enable(&_clock_cl_dvfs); +} + +void clock_enable_coresight() +{ + clock_enable(&_clock_coresight); +} + +#define L_SWR_SDMMC1_RST (1<<14) +#define L_SWR_SDMMC2_RST (1<<9) +#define L_SWR_SDMMC4_RST (1<<15) +#define U_SWR_SDMMC3_RST (1<<5) + +#define L_CLK_ENB_SDMMC1 (1<<14) +#define L_CLK_ENB_SDMMC2 (1<<9) +#define L_CLK_ENB_SDMMC4 (1<<15) +#define U_CLK_ENB_SDMMC3 (1<<5) + +#define L_SET_SDMMC1_RST (1<<14) +#define L_SET_SDMMC2_RST (1<<9) +#define L_SET_SDMMC4_RST (1<<15) +#define U_SET_SDMMC3_RST (1<<5) + +#define L_CLR_SDMMC1_RST (1<<14) +#define L_CLR_SDMMC2_RST (1<<9) +#define L_CLR_SDMMC4_RST (1<<15) +#define U_CLR_SDMMC3_RST (1<<5) + +#define L_SET_CLK_ENB_SDMMC1 (1<<14) +#define L_SET_CLK_ENB_SDMMC2 (1<<9) +#define L_SET_CLK_ENB_SDMMC4 (1<<15) +#define U_SET_CLK_ENB_SDMMC3 (1<<5) + +#define L_CLR_CLK_ENB_SDMMC1 (1<<14) +#define L_CLR_CLK_ENB_SDMMC2 (1<<9) +#define L_CLR_CLK_ENB_SDMMC4 (1<<15) +#define U_CLR_CLK_ENB_SDMMC3 (1<<5) + +static int _clock_sdmmc_is_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC1_RST; + case SDMMC_2: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC2_RST; + case SDMMC_3: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_U) & U_SWR_SDMMC3_RST; + case SDMMC_4: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC4_RST; + } + return 0; +} + +static void _clock_sdmmc_set_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC1_RST; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC2_RST; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_SET) = U_SET_SDMMC3_RST; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC4_RST; + } +} + +static void _clock_sdmmc_clear_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC1_RST; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC2_RST; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_CLR) = U_CLR_SDMMC3_RST; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC4_RST; + } +} + +static int _clock_sdmmc_is_enabled(u32 id) +{ + switch (id) + { + case SDMMC_1: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC1; + case SDMMC_2: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC2; + case SDMMC_3: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) & U_CLK_ENB_SDMMC3; + case SDMMC_4: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC4; + } + return 0; +} + +static void _clock_sdmmc_set_enable(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC1; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC2; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_SET) = U_SET_CLK_ENB_SDMMC3; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC4; + } +} + +static void _clock_sdmmc_clear_enable(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC1; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC2; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_CLR) = U_CLR_CLK_ENB_SDMMC3; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC4; + } +} + +static u32 _clock_sdmmc_table[8] = { 0 }; + +static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val) +{ + u32 divisor = 0; + u32 source = 0; + + switch (val) + { + case 25000: + *pout = 24728; + divisor = 31; + break; + case 26000: + *pout = 25500; + divisor = 30; + break; + case 40800: + *pout = 40800; + divisor = 18; + break; + case 50000: + *pout = 48000; + divisor = 15; + break; + case 52000: + *pout = 51000; + divisor = 14; + break; + case 100000: + *pout = 90667; + divisor = 7; + break; + case 200000: + *pout = 163200; + divisor = 3; + break; + case 208000: + *pout = 204000; + divisor = 2; + break; + default: + return 0; + } + + _clock_sdmmc_table[2 * id] = val; + _clock_sdmmc_table[2 * id + 1] = *pout; + + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1) = source | divisor; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2) = source | divisor; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3) = source | divisor; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4) = source | divisor; + break; + } + + return 1; +} + +void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val) +{ + if (_clock_sdmmc_table[2 * id] == val) + { + *pout = _clock_sdmmc_table[2 * id + 1]; + } + else + { + int is_enabled = _clock_sdmmc_is_enabled(id); + if (is_enabled) + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_config_clock_source_inner(pout, id, val); + if (is_enabled) + _clock_sdmmc_set_enable(id); + _clock_sdmmc_is_reset(id); + } +} + +void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type) +{ + switch (type) + { + case 0: + *pout = 26000; + *pdivisor = 66; + break; + case 1: + *pout = 26000; + *pdivisor = 1; + break; + case 2: + *pout = 52000; + *pdivisor = 1; + break; + case 3: + case 4: + case 11: + *pout = 200000; + *pdivisor = 1; + break; + case 5: + *pout = 25000; + *pdivisor = 64; + case 6: + case 8: + *pout = 25000; + *pdivisor = 1; + break; + case 7: + *pout = 50000; + *pdivisor = 1; + case 10: + *pout = 100000; + *pdivisor = 1; + case 13: + *pout = 40800; + *pdivisor = 1; + break; + case 14: + *pout = 200000; + *pdivisor = 2; + break; + } +} + +int clock_sdmmc_is_not_reset_and_enabled(u32 id) +{ + return !_clock_sdmmc_is_reset(id) && _clock_sdmmc_is_enabled(id); +} + +void clock_sdmmc_enable(u32 id, u32 val) +{ + u32 div = 0; + + if (_clock_sdmmc_is_enabled(id)) + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_set_reset(id); + _clock_sdmmc_config_clock_source_inner(&div, id, val); + _clock_sdmmc_set_enable(id); + _clock_sdmmc_is_reset(id); + sleep((100000 + div - 1) / div); + _clock_sdmmc_clear_reset(id); + _clock_sdmmc_is_reset(id); +} + +void clock_sdmmc_disable(u32 id) +{ + _clock_sdmmc_set_reset(id); + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_is_reset(id); +} diff --git a/src/hwinit/clock.h b/src/hwinit/clock.h new file mode 100644 index 0000000..d3c6a04 --- /dev/null +++ b/src/hwinit/clock.h @@ -0,0 +1,108 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _CLOCK_H_ +#define _CLOCK_H_ + +#include "types.h" + +/*! Clock registers. */ +#define CLK_RST_CONTROLLER_RST_DEVICES_L 0x4 +#define CLK_RST_CONTROLLER_RST_DEVICES_U 0xC +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L 0x10 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_H 0x14 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U 0x18 +#define CLK_RST_CONTROLLER_CCLK_BURST_POLICY 0x20 +#define CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER 0x24 +#define CLK_RST_CONTROLLER_SCLK_BURST_POLICY 0x28 +#define CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER 0x2C +#define CLK_RST_CONTROLLER_CLK_SYSTEM_RATE 0x30 +#define CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48 +#define CLK_RST_CONTROLLER_OSC_CTRL 0x50 +#define CLK_RST_CONTROLLER_PLLX_BASE 0xE0 +#define CLK_RST_CONTROLLER_PLLX_MISC 0xE4 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 0x150 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 0x154 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 0x164 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 0x1BC +#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280 +#define CLK_RST_CONTROLLER_CLK_ENB_X_SET 0x284 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y 0x298 +#define CLK_RST_CONTROLLER_CLK_ENB_Y_SET 0x29C +#define CLK_RST_CONTROLLER_RST_DEV_L_SET 0x300 +#define CLK_RST_CONTROLLER_RST_DEV_L_CLR 0x304 +#define CLK_RST_CONTROLLER_RST_DEV_H_SET 0x308 +#define CLK_RST_CONTROLLER_RST_DEV_U_SET 0x310 +#define CLK_RST_CONTROLLER_RST_DEV_U_CLR 0x314 +#define CLK_RST_CONTROLLER_CLK_ENB_L_SET 0x320 +#define CLK_RST_CONTROLLER_CLK_ENB_L_CLR 0x324 +#define CLK_RST_CONTROLLER_CLK_ENB_H_SET 0x328 +#define CLK_RST_CONTROLLER_CLK_ENB_U_SET 0x330 +#define CLK_RST_CONTROLLER_CLK_ENB_U_CLR 0x334 +#define CLK_RST_CONTROLLER_RST_DEVICES_V 0x358 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_V 0x360 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W 0x364 +#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 0x388 +#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT 0x3B4 +#define CLK_RST_CONTROLLER_CLK_ENB_V_SET 0x440 +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR 0x454 +#define CLK_RST_CONTROLLER_PLLX_MISC_3 0x518 +#define CLK_RST_CONTROLLER_SPARE_REG0 0x55C +#define CLK_RST_CONTROLLER_PLLMB_BASE 0x5E8 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM 0x694 + +/*! Generic clock descriptor. */ +typedef struct _clock_t +{ + u32 reset; + u32 enable; + u32 source; + u8 index; + u8 clk_src; + u8 clk_div; +} clock_t; + +/*! Generic clock enable/disable. */ +void clock_enable(const clock_t *clk); +void clock_disable(const clock_t *clk); + +/*! Clock control for specific hardware portions. */ +void clock_enable_fuse(u32 enable); +void clock_enable_uart(u32 idx); +void clock_enable_i2c(u32 idx); +void clock_enable_se(); +void clock_enable_host1x(); +void clock_disable_host1x(); +void clock_enable_tsec(); +void clock_disable_tsec(); +void clock_enable_sor_safe(); +void clock_disable_sor_safe(); +void clock_enable_sor0(); +void clock_disable_sor0(); +void clock_enable_sor1(); +void clock_disable_sor1(); +void clock_enable_kfuse(); +void clock_disable_kfuse(); +void clock_enable_cl_dvfs(); +void clock_enable_coresight(); +void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val); +void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type); +int clock_sdmmc_is_not_reset_and_enabled(u32 id); +void clock_sdmmc_enable(u32 id, u32 val); +void clock_sdmmc_disable(u32 id); + +#endif diff --git a/src/hwinit/cluster.c b/src/hwinit/cluster.c new file mode 100644 index 0000000..f88615e --- /dev/null +++ b/src/hwinit/cluster.c @@ -0,0 +1,127 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "cluster.h" +#include "i2c.h" +#include "clock.h" +#include "util.h" +#include "pmc.h" +#include "t210.h" +#include "max77620.h" + +void _cluster_enable_power() +{ + u8 tmp = i2c_recv_byte(I2C_5, 0x3C, MAX77620_REG_AME_GPIO); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_AME_GPIO, tmp & 0xDF); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_GPIO5, 0x09); + + //Enable cores power. + i2c_send_byte(I2C_5, 0x1B, 0x2, 0x20); + i2c_send_byte(I2C_5, 0x1B, 0x3, 0x8D); + i2c_send_byte(I2C_5, 0x1B, 0x0, 0xB7); + i2c_send_byte(I2C_5, 0x1B, 0x1, 0xB7); +} + +int _cluster_pmc_enable_partition(u32 part, u32 toggle) +{ + //Check if the partition has already been turned on. + if (PMC(APBDEV_PMC_PWRGATE_STATUS) & part) + return 1; + + u32 i = 5001; + while (PMC(APBDEV_PMC_PWRGATE_TOGGLE) & 0x100) + { + sleep(1); + i--; + if (i < 1) + return 0; + } + + PMC(APBDEV_PMC_PWRGATE_TOGGLE) = toggle | 0x100; + + i = 5001; + while (i > 0) + { + if (PMC(APBDEV_PMC_PWRGATE_STATUS) & part) + break; + sleep(1); + i--; + } + + return 1; +} + +void cluster_boot_cpu0(u32 entry) +{ + //Set ACTIVE_CLUSER to FAST. + FLOW_CTLR(FLOW_CTLR_BPMP_CLUSTER_CONTROL) &= 0xFFFFFFFE; + + _cluster_enable_power(); + + if (!(CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) & 0x40000000)) + { + CLOCK(CLK_RST_CONTROLLER_PLLX_MISC_3) &= 0xFFFFFFF7; + sleep(2); + CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = 0x80404E02; + CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = 0x404E02; + CLOCK(CLK_RST_CONTROLLER_PLLX_MISC) = (CLOCK(CLK_RST_CONTROLLER_PLLX_MISC) & 0xFFFBFFFF) | 0x40000; + CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = 0x40404E02; + } + while (!(CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) & 0x8000000)) + ; + + //Configure MSELECT source and enable clock. + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT) & 0x1FFFFF00) | 6; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) = (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) & 0xFFFFFFF7) | 8; + + //Configure initial CPU clock frequency and enable clock. + CLOCK(CLK_RST_CONTROLLER_CCLK_BURST_POLICY) = 0x20008888; + CLOCK(CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER) = 0x80000000; + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_V_SET) = 1; + + clock_enable_coresight(); + + //CAR2PMC_CPU_ACK_WIDTH should be set to 0. + CLOCK(CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2) &= 0xFFFFF000; + + //Enable CPU rail. + _cluster_pmc_enable_partition(1, 0); + //Enable cluster 0 non-CPU. + _cluster_pmc_enable_partition(0x8000, 15); + //Enable CE0. + _cluster_pmc_enable_partition(0x4000, 14); + + //Request and wait for RAM repair. + FLOW_CTLR(FLOW_CTLR_RAM_REPAIR) = 1; + while (!(FLOW_CTLR(FLOW_CTLR_RAM_REPAIR) & 2)) + ; + + EXCP_VEC(0x100) = 0; + + //Set reset vector. + SB(SB_AA64_RESET_LOW) = entry | 1; + SB(SB_AA64_RESET_HIGH) = 0; + //Non-secure reset vector write disable. + SB(SB_CSR) = 2; + (void)SB(SB_CSR); + + //Clear MSELECT reset. + CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_V) &= 0xFFFFFFF7; + //Clear NONCPU reset. + CLOCK(CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR) = 0x20000000; + //Clear CPU{0,1,2,3} POR and CORE, CX0, L2, and DBG reset. + CLOCK(CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR) = 0x411F000F; +} diff --git a/src/hwinit/cluster.h b/src/hwinit/cluster.h new file mode 100644 index 0000000..623ae7c --- /dev/null +++ b/src/hwinit/cluster.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _CLUSTER_H_ +#define _CLUSTER_H_ + +#include "types.h" + +/*! Flow controller registers. */ +#define FLOW_CTLR_RAM_REPAIR 0x40 +#define FLOW_CTLR_BPMP_CLUSTER_CONTROL 0x98 + +void cluster_boot_cpu0(u32 entry); + +#endif diff --git a/src/hwinit/di.c b/src/hwinit/di.c new file mode 100644 index 0000000..c8634d3 --- /dev/null +++ b/src/hwinit/di.c @@ -0,0 +1,173 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "di.h" +#include "t210.h" +#include "util.h" +#include "i2c.h" +#include "pmc.h" +#include "gpio.h" +#include "pinmux.h" +#include "max77620.h" + +#include "di.inl" + +static u32 _display_ver = 0; + +static void _display_dsi_wait(u32 timeout, u32 off, u32 mask) +{ + u32 end = TMR(0x10) + timeout; + while (TMR(0x10) < end && DSI(off) & mask) + ; + sleep(5); +} + +void display_init() +{ + //Power on. + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_LDO0_CFG, 0xD0); //Configure to 1.2V. + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_GPIO7, 0x09); + + //Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. + CLOCK(0x30C) = 0x1010000; + CLOCK(0x328) = 0x1010000; + CLOCK(0x304) = 0x18000000; + CLOCK(0x320) = 0x18000000; + CLOCK(0x284) = 0x20000; + CLOCK(0x66C) = 0xA; + CLOCK(0x448) = 0x80000; + CLOCK(0x620) = 0xA; + + //DPD idle. + PMC(APBDEV_PMC_IO_DPD_REQ) = 0x40000000; + PMC(APBDEV_PMC_IO_DPD2_REQ) = 0x40000000; + + //Config pins. + PINMUX_AUX(0x1D0) &= 0xFFFFFFEF; + PINMUX_AUX(0x1D4) &= 0xFFFFFFEF; + PINMUX_AUX(0x1FC) &= 0xFFFFFFEF; + PINMUX_AUX(0x200) &= 0xFFFFFFEF; + PINMUX_AUX(0x204) &= 0xFFFFFFEF; + + GPIO_3(0x00) = GPIO_3(0x00) & 0xFFFFFFFC | 0x3; + GPIO_3(0x10) = GPIO_3(0x10) & 0xFFFFFFFC | 0x3; + GPIO_3(0x20) = GPIO_3(0x20) & 0xFFFFFFFE | 0x1; + + sleep(10000u); + + GPIO_3(0x20) = GPIO_3(0x20) & 0xFFFFFFFD | 0x2; + + sleep(10000); + + GPIO_6(0x04) = GPIO_6(0x04) & 0xFFFFFFF8 | 0x7; + GPIO_6(0x14) = GPIO_6(0x14) & 0xFFFFFFF8 | 0x7; + GPIO_6(0x24) = GPIO_6(0x24) & 0xFFFFFFFD | 0x2; + + //Config display interface and display. + MIPI_CAL(0x60) = 0; + + exec_cfg((u32 *)CLOCK_BASE, _display_config_1, 4); + exec_cfg((u32 *)DISPLAY_A_BASE, _display_config_2, 94); + exec_cfg((u32 *)DSI_BASE, _display_config_3, 60); + + sleep(10000); + + GPIO_6(0x24) = GPIO_6(0x24) & 0xFFFFFFFB | 0x4; + + sleep(60000); + + DSI(_DSIREG(DSI_DSI_BTA_TIMING)) = 0x50204; + DSI(_DSIREG(DSI_DSI_WR_DATA)) = 0x337; + DSI(_DSIREG(DSI_DSI_TRIGGER)) = 0x2; + _display_dsi_wait(250000, _DSIREG(DSI_DSI_TRIGGER), 3); + + DSI(_DSIREG(DSI_DSI_WR_DATA)) = 0x406; + DSI(_DSIREG(DSI_DSI_TRIGGER)) = 0x2; + _display_dsi_wait(250000, _DSIREG(DSI_DSI_TRIGGER), 3); + + DSI(_DSIREG(DSI_HOST_DSI_CONTROL)) = 0x200B; + _display_dsi_wait(150000, _DSIREG(DSI_HOST_DSI_CONTROL), 8); + + sleep(5000); + + _display_ver = DSI(_DSIREG(DSI_DSI_RD_DATA)); + if (_display_ver == 0x10) + exec_cfg((u32 *)DSI_BASE, _display_config_4, 43); + + DSI(_DSIREG(DSI_DSI_WR_DATA)) = 0x1105; + DSI(_DSIREG(DSI_DSI_TRIGGER)) = 0x2; + + sleep(180000); + + DSI(_DSIREG(DSI_DSI_WR_DATA)) = 0x2905; + DSI(_DSIREG(DSI_DSI_TRIGGER)) = 0x2; + + sleep(20000); + + exec_cfg((u32 *)DSI_BASE, _display_config_5, 21); + exec_cfg((u32 *)CLOCK_BASE, _display_config_6, 3); + DISPLAY_A(_DIREG(DC_DISP_DISP_CLOCK_CONTROL)) = 4; + exec_cfg((u32 *)DSI_BASE, _display_config_7, 10); + + sleep(10000); + + exec_cfg((u32 *)MIPI_CAL_BASE, _display_config_8, 6); + exec_cfg((u32 *)DSI_BASE, _display_config_9, 4); + exec_cfg((u32 *)MIPI_CAL_BASE, _display_config_10, 16); + + sleep(10000); + + exec_cfg((u32 *)DISPLAY_A_BASE, _display_config_11, 113); +} + +void display_end() +{ + gpio_write(GPIO_PORT_V, GPIO_PIN_0, GPIO_LOW); //Backlight PWM. + + gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); //Backlight PWM. + + PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_TRISTATE) | PINMUX_TRISTATE; + PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) >> 2) << 2 | 1; +} + +void display_color_screen(u32 color) +{ + exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_one_color, 8); + + //Configure display to show single color. + DISPLAY_A(_DIREG(DC_WIN_AD_WIN_OPTIONS)) = 0; + DISPLAY_A(_DIREG(DC_WIN_BD_WIN_OPTIONS)) = 0; + DISPLAY_A(_DIREG(DC_WIN_CD_WIN_OPTIONS)) = 0; + DISPLAY_A(_DIREG(DC_DISP_BLEND_BACKGROUND_COLOR)) = color; + DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) = DISPLAY_A(_DIREG(DC_CMD_STATE_CONTROL)) & 0xFFFFFFFE | 1; + + sleep(35000); + + GPIO_6(0x24) = GPIO_6(0x24) & 0xFFFFFFFE | 1; +} + +u32 *display_init_framebuffer() +{ + //This configures the framebuffer @ 0xC0000000 with a resolution of 1280x720 (line stride 768). + exec_cfg((u32 *)DISPLAY_A_BASE, cfg_display_framebuffer, 32); + + sleep(35000); + + GPIO_6(0x24) = GPIO_6(0x24) & 0xFFFFFFFE | 1; + + return (u32 *)0xC0000000; +} diff --git a/src/hwinit/di.h b/src/hwinit/di.h new file mode 100644 index 0000000..29e3ebf --- /dev/null +++ b/src/hwinit/di.h @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _DI_H_ +#define _DI_H_ + +#include "types.h" + +/*! Display registers. */ +#define _DIREG(reg) ((reg) * 4) +#define DC_CMD_DISPLAY_COMMAND 0x32 +#define DC_CMD_STATE_ACCESS 0x40 +#define DC_CMD_STATE_CONTROL 0x41 +#define DC_CMD_DISPLAY_WINDOW_HEADER 0x42 +#define DC_DISP_DISP_WIN_OPTIONS 0x402 +#define DC_DISP_DISP_CLOCK_CONTROL 0x42E +#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4E4 +#define DC_WIN_AD_WIN_OPTIONS 0xB80 +#define DC_WIN_BD_WIN_OPTIONS 0xD80 +#define DC_WIN_CD_WIN_OPTIONS 0xF80 + +//The following registers are A/B/C shadows of the 0xB80/0xD80/0xF80 registers (see DISPLAY_WINDOW_HEADER). +#define DC_X_WIN_XD_WIN_OPTIONS 0x700 +#define DC_X_WIN_XD_COLOR_DEPTH 0x703 +#define DC_X_WIN_XD_POSITION 0x704 +#define DC_X_WIN_XD_SIZE 0x705 +#define DC_X_WIN_XD_PRESCALED_SIZE 0x706 +#define DC_X_WIN_XD_H_INITIAL_DDA 0x707 +#define DC_X_WIN_XD_V_INITIAL_DDA 0x708 +#define DC_X_WIN_XD_DDA_INCREMENT 0x709 +#define DC_X_WIN_XD_LINE_STRIDE 0x70A + +//The following registers are A/B/C shadows of the 0xBC0/0xDC0/0xFC0 registers (see DISPLAY_WINDOW_HEADER). +#define DC_X_WINBUF_XD_START_ADDR 0x800 +#define DC_X_WINBUF_XD_ADDR_H_OFFSET 0x806 +#define DC_X_WINBUF_XD_ADDR_V_OFFSET 0x808 +#define DC_X_WINBUF_XD_SURFACE_KIND 0x80B + +/*! Display serial interface registers. */ +#define _DSIREG(reg) ((reg) * 4) +#define DSI_DSI_RD_DATA 0x9 +#define DSI_DSI_WR_DATA 0xA +#define DSI_DSI_POWER_CONTROL 0xB +#define DSI_HOST_DSI_CONTROL 0xF +#define DSI_DSI_TRIGGER 0x13 +#define DSI_DSI_BTA_TIMING 0x3F +#define DSI_PAD_CONTROL 0x4B +#define DSI_DSI_VID_MODE_CONTROL 0x4E + +void display_init(); +void display_end(); + +/*! Show one single color on the display. */ +void display_color_screen(u32 color); + +/*! Init display in full 1280x720 resolution (32bpp, line stride 768, framebuffer size = 1280*768*4 bytes). */ +u32 *display_init_framebuffer(); + +#endif diff --git a/src/hwinit/di.inl b/src/hwinit/di.inl new file mode 100644 index 0000000..7c9cffa --- /dev/null +++ b/src/hwinit/di.inl @@ -0,0 +1,548 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +//Clock config. +static const cfg_op_t _display_config_1[4] = { + {0x4E, 0x40000000}, //CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 + {0x34, 0x4830A001}, //CLK_RST_CONTROLLER_PLLD_BASE + {0x36, 0x20}, //CLK_RST_CONTROLLER_PLLD_MISC1 + {0x37, 0x2D0AAA} //CLK_RST_CONTROLLER_PLLD_MISC +}; + +//Display A config. +static const cfg_op_t _display_config_2[94] = { + {0x40, 0}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0x43, 0x54}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0x42, 0x10}, + {0x42, 0x20}, + {0x42, 0x40}, + {0x480, 0}, + {0x403, 0}, + {0x404, 0}, + {0x36, 0x50155}, + {1, 0x100}, + {0x28, 0x109}, + {DC_CMD_STATE_CONTROL, 0xF00}, + {DC_CMD_STATE_CONTROL, 0xF}, + {0x40, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x10}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x10}, + {0x42, 0x10}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x20}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x20}, + {0x42, 0x20}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x42, 0x40}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x40}, + {0x42, 0x40}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x430, 8}, + {0x42F, 0}, + {0x307, 0x1000000}, + {0x309, 0}, + {0x4E4, 0}, + {0x300, 0}, + {DC_CMD_STATE_CONTROL, 0xF00}, + {DC_CMD_STATE_CONTROL, 0xF}, + {0x42, 0x10}, + {0x716, 0x10000FF}, + {0x42, 0x20}, + {0x716, 0x10000FF}, + {0x42, 0x40}, + {0x716, 0x10000FF}, + {0x31, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x402, 0}, + {0x32, 0}, + {DC_CMD_STATE_CONTROL, 0xF00}, + {DC_CMD_STATE_CONTROL, 0xF} +}; + +//DSI config. +static const cfg_op_t _display_config_3[60] = { + {0xA, 0}, + {0xC, 0}, + {0xD, 0}, + {0xE, 0}, + {0x1B, 0}, + {0x1C, 0}, + {0x1D, 0}, + {0x1E, 0}, + {0x33, 0}, + {0x23, 0}, + {0x25, 0}, + {0x27, 0}, + {0x29, 0}, + {0x2B, 0}, + {0x2D, 0}, + {0x24, 0}, + {0x26, 0}, + {0x28, 0}, + {0x2A, 0}, + {0x2C, 0}, + {0x2E, 0}, + {0x10, 0}, + {0x4C, 0}, + {0x11, 0x18}, + {0x12, 0x1E0}, + {0x13, 0}, + {0x1A, 0}, + {0x34, 0}, + {0x35, 0}, + {0x36, 0}, + {0x37, 0}, + {0x4F, 0}, + {0x3C, 0x6070601}, + {0x3D, 0x40A0E05}, + {0x3E, 0x30109}, + {0x3F, 0x190A14}, + {0x44, 0x2000FFFF}, + {0x45, 0x7652000}, + {0x46, 0}, + {0x4B, 0}, + {DSI_DSI_POWER_CONTROL, 1}, + {DSI_DSI_POWER_CONTROL, 1}, + {DSI_DSI_POWER_CONTROL, 0}, + {DSI_DSI_POWER_CONTROL, 0}, + {0x4F, 0}, + {0x3C, 0x6070601}, + {0x3D, 0x40A0E05}, + {0x3E, 0x30118}, + {0x3F, 0x190A14}, + {0x44, 0x2000FFFF}, + {0x45, 0x13432000}, + {0x46, 0}, + {0xF, 0x102003}, + {0x10, 0x31}, + {DSI_DSI_POWER_CONTROL, 1}, + {DSI_DSI_POWER_CONTROL, 1}, + {0x12, 0x40}, + {0x13, 0}, + {0x14, 0}, + {0x1A, 0} +}; + +//DSI config (if ver == 0x10). +static const cfg_op_t _display_config_4[43] = { + {DSI_DSI_WR_DATA, 0x439}, + {DSI_DSI_WR_DATA, 0x9483FFB9}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0xBD15}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x1939}, + {DSI_DSI_WR_DATA, 0xAAAAAAD8}, + {DSI_DSI_WR_DATA, 0xAAAAAAEB}, + {DSI_DSI_WR_DATA, 0xAAEBAAAA}, + {DSI_DSI_WR_DATA, 0xAAAAAAAA}, + {DSI_DSI_WR_DATA, 0xAAAAAAEB}, + {DSI_DSI_WR_DATA, 0xAAEBAAAA}, + {DSI_DSI_WR_DATA, 0xAA}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x1BD15}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x2739}, + {DSI_DSI_WR_DATA, 0xFFFFFFD8}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFF}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x2BD15}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0xF39}, + {DSI_DSI_WR_DATA, 0xFFFFFFD8}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFFFF}, + {DSI_DSI_WR_DATA, 0xFFFFFF}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0xBD15}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x6D915}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x439}, + {DSI_DSI_WR_DATA, 0xB9}, + {DSI_DSI_TRIGGER, 2} +}; + +//DSI config. +static const cfg_op_t _display_config_5[21] = { + {0x4F, 0}, + {0x3C, 0x6070601}, + {0x3D, 0x40A0E05}, + {0x3E, 0x30172}, + {0x3F, 0x190A14}, + {0x44, 0x20000A40}, + {0x45, 0x5A2F2000}, + {0x46, 0}, + {0x23, 0x40000208}, + {0x27, 0x40000308}, + {0x2B, 0x40000308}, + {0x25, 0x40000308}, + {0x29, 0x3F3B2B08}, + {0x2A, 0x2CC}, + {0x2D, 0x3F3B2B08}, + {0x2E, 0x2CC}, + {0x34, 0xCE0000}, + {0x35, 0x87001A2}, + {0x36, 0x190}, + {0x37, 0x190}, + {0xF, 0}, +}; + +//Clock config. +static const cfg_op_t _display_config_6[3] = { + {0x34, 0x4810C001}, //CLK_RST_CONTROLLER_PLLD_BASE + {0x36, 0x20}, //CLK_RST_CONTROLLER_PLLD_MISC1 + {0x37, 0x2DFC00} //CLK_RST_CONTROLLER_PLLD_MISC +}; + +//DSI config. +static const cfg_op_t _display_config_7[10] = { + {0x13, 0}, + {0x10, 0}, + {0x11, 6}, + {0x12, 0x1E0}, + {DSI_DSI_POWER_CONTROL, 1}, + {0x10, 0x103032}, + {0xF, 0x33}, + {0x10, 0x103032}, + {0xF, 3}, + {0xF, 0x23} +}; + +//MIPI CAL config. +static const cfg_op_t _display_config_8[6] = { + {0x18, 0}, + {2, 0xF3F10000}, + {0x16, 1}, + {0x18, 0}, + {0x18, 0x10010}, + {0x17, 0x300} +}; + +//DSI config. +static const cfg_op_t _display_config_9[4] = { + {0x4F, 0}, + {0x50, 0}, + {0x51, 0x3333}, + {0x52, 0} +}; + +//MIPI CAL config. +static const cfg_op_t _display_config_10[16] = { + {0xE, 0x200200}, + {0xF, 0x200200}, + {0x19, 0x200002}, + {0x1A, 0x200002}, + {5, 0}, + {6, 0}, + {7, 0}, + {8, 0}, + {9, 0}, + {0xA, 0}, + {0x10, 0}, + {0x11, 0}, + {0x1A, 0}, + {0x1C, 0}, + {0x1D, 0}, + {0, 0x2A000001} +}; + +//Display A config. +static const cfg_op_t _display_config_11[113] = { + {0x40, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x10}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x10}, + {0x42, 0x10}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x20}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x20}, + {0x42, 0x20}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x42, 0x40}, + {0x70E, 0}, + {0x700, 0}, + {0x42, 0x40}, + {0x42, 0x40}, + {0x611, 0xF0}, + {0x612, 0x12A}, + {0x613, 0}, + {0x614, 0x198}, + {0x615, 0x39B}, + {0x616, 0x32F}, + {0x617, 0x204}, + {0x618, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x430, 8}, + {0x42F, 0}, + {0x307, 0x1000000}, + {0x309, 0}, + {0x4E4, 0}, + {0x300, 0}, + {DC_CMD_STATE_CONTROL, 0xF00}, + {DC_CMD_STATE_CONTROL, 0xF}, + {0x42, 0x10}, + {0x716, 0x10000FF}, + {0x42, 0x20}, + {0x716, 0x10000FF}, + {0x42, 0x40}, + {0x716, 0x10000FF}, + {0x31, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x402, 0}, + {0x32, 0}, + {DC_CMD_STATE_CONTROL, 0xF00}, + {DC_CMD_STATE_CONTROL, 0xF}, + {0x40, 0}, + {0x405, 0}, + {0x406, 0x10000}, + {0x407, 0x10048}, + {0x408, 0x90048}, + {0x409, 0x50002D0}, + {0x40A, 0xA0088}, + {0x431, 0x10001}, + {0x303, 0}, + {0x432, 5}, + {0x42F, 0}, + {0x42E, 0}, + {0x31, 0}, + {0x42, 0x10}, + {0x700, 0}, + {0x42, 0x20}, + {0x700, 0}, + {0x42, 0x40}, + {0x700, 0}, + {0x402, 0}, + {0x32, 0x20}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0x40, 5}, + {0x40A, 0xA0088}, + {0x40, 0}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0, 0x301}, + {0, 0x301}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0x40, 0}, + {0x42E, 4}, + {0x430, 8}, + {0x31, 0} +}; + +////Display A config. +static const cfg_op_t _display_config_12[17] = { + {0x40A, 0xA0088}, + {0x38, 0}, + {0x40, 0}, + {0x39, 0}, + {0x28, 0}, + {0x32, 0}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0, 0x301}, + {0, 0x301}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1}, + {0x36, 0}, + {DC_CMD_STATE_CONTROL, 0x100}, + {DC_CMD_STATE_CONTROL, 1} +}; + +//DSI config. +static const cfg_op_t _display_config_13[16] = { + {DSI_DSI_POWER_CONTROL, 0}, + {0x4F, 0}, + {0x3C, 0x6070601}, + {0x3D, 0x40A0E05}, + {0x3E, 0x30109}, + {0x3F, 0x190A14}, + {0x44, 0x2000FFFF}, + {0x45, 0x7652000}, + {0x46, 0}, + {0xF, 0x102003}, + {0x10, 0x31}, + {DSI_DSI_POWER_CONTROL, 1}, + {0x12, 0x40}, + {0x13, 0}, + {0x14, 0}, + {0x1A, 0} +}; + +//DSI config (if ver == 0x10). +static const cfg_op_t _display_config_14[22] = { + {DSI_DSI_WR_DATA, 0x439}, + {DSI_DSI_WR_DATA, 0x9483FFB9}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x2139}, + {DSI_DSI_WR_DATA, 0x191919D5}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19191919}, + {DSI_DSI_WR_DATA, 0x19}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0xB39}, + {DSI_DSI_WR_DATA, 0x4F0F41B1}, + {DSI_DSI_WR_DATA, 0xF179A433}, + {DSI_DSI_WR_DATA, 0x2D81}, + {DSI_DSI_TRIGGER, 2}, + {DSI_DSI_WR_DATA, 0x439}, + {DSI_DSI_WR_DATA, 0xB9}, + {DSI_DSI_TRIGGER, 2} +}; + +//Display A config. +static const cfg_op_t cfg_display_one_color[8] = { + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x10}, //Enable window A. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x20}, //Enable window B. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x40}, //Enable window C. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_DISP_DISP_WIN_OPTIONS, 0x20000000}, //DSI_ENABLE + {DC_CMD_DISPLAY_COMMAND, 0x20} //DISPLAY_CTRL_MODE: continuous display. +}; + +//Display A config. +static const cfg_op_t cfg_display_framebuffer[32] = { + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x40}, //Enable window C. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x20}, //Enable window B. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_CMD_DISPLAY_WINDOW_HEADER, 0x10}, //Enable window A. + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_DISP_DISP_WIN_OPTIONS, 0x20000000}, //DSI_ENABLE + {DC_X_WIN_XD_COLOR_DEPTH, 0xD}, //T_A8B8G8R8 + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_X_WIN_XD_POSITION, 0}, //(0,0) + {DC_X_WIN_XD_H_INITIAL_DDA, 0}, + {DC_X_WIN_XD_V_INITIAL_DDA, 0}, + {DC_X_WIN_XD_PRESCALED_SIZE, 0x5000B40}, //Pre-scaled size: 1280x2880 bytes (= 0x500 vertical lines x 0xB40 bytes). + {DC_X_WIN_XD_DDA_INCREMENT, 0x10001000}, + {DC_X_WIN_XD_SIZE, 0x50002D0}, //Window size: 1280x720 (= 0x500 vertical lines x 0x2D0 horizontal pixels). + {DC_X_WIN_XD_LINE_STRIDE, 0x6000C00}, //768*2x768*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements. + {0x702, 0}, + {DC_X_WINBUF_XD_SURFACE_KIND, 0}, //Regular surface. + {DC_X_WINBUF_XD_START_ADDR, 0xC0000000}, //Framebuffer address. + {DC_X_WINBUF_XD_ADDR_H_OFFSET, 0}, + {DC_X_WINBUF_XD_ADDR_V_OFFSET, 0}, + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_DISP_DISP_WIN_OPTIONS, 0x20000000}, //DSI_ENABLE + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_DISP_DISP_WIN_OPTIONS, 0x20000000}, //DSI_ENABLE + {DC_X_WIN_XD_WIN_OPTIONS, 0}, + {DC_DISP_DISP_WIN_OPTIONS, 0x20000000}, //DSI_ENABLE + {DC_X_WIN_XD_WIN_OPTIONS, 0x40000000}, //Enable window AD. + {DC_CMD_DISPLAY_COMMAND, 0x20}, //DISPLAY_CTRL_MODE: continuous display. + {DC_CMD_STATE_CONTROL, 0x300}, //General update; window A update. + {DC_CMD_STATE_CONTROL, 3} //General activation request; window A activation request. +}; diff --git a/src/hwinit/diskio.c b/src/hwinit/diskio.c new file mode 100644 index 0000000..5bddd90 --- /dev/null +++ b/src/hwinit/diskio.c @@ -0,0 +1,71 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ +/*-----------------------------------------------------------------------*/ +/* If a working storage control module is available, it should be */ +/* attached to the FatFs via a glue function rather than modifying it. */ +/* This is an example of glue functions to attach various exsisting */ +/* storage control modules to the FatFs module with a defined API. */ +/*-----------------------------------------------------------------------*/ + +#include +#include "diskio.h" /* FatFs lower layer API */ +#include "sdmmc.h" + +extern sdmmc_storage_t sd_storage; + +DSTATUS disk_status ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + return 0; +} + +DSTATUS disk_initialize ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + return 0; +} + +DRESULT disk_read ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to read */ +) +{ + if ((u32)buff >= 0x90000000) + return sdmmc_storage_read(&sd_storage, sector, count, buff) ? RES_OK : RES_ERROR; + u8 *buf = (u8 *)0x98000000; //TODO: define this somewhere. + if (sdmmc_storage_read(&sd_storage, sector, count, buf)) + { + memcpy(buff, buf, 512 * count); + return RES_OK; + } + return RES_ERROR; +} + +DRESULT disk_write ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to write */ +) +{ + if ((u32)buff >= 0x90000000) + return sdmmc_storage_write(&sd_storage, sector, count, (void *)buff) ? RES_OK : RES_ERROR; + u8 *buf = (u8 *)0x98000000; //TODO: define this somewhere. + memcpy(buf, buff, 512 * count); + if (sdmmc_storage_write(&sd_storage, sector, count, buf)) + return RES_OK; + return RES_ERROR; +} + +DRESULT disk_ioctl ( + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + return RES_OK; +} diff --git a/src/hwinit/diskio.h b/src/hwinit/diskio.h new file mode 100644 index 0000000..20ebde9 --- /dev/null +++ b/src/hwinit/diskio.h @@ -0,0 +1,80 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ +#define ISDIO_READ 55 /* Read data form SD iSDIO register */ +#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hwinit/emc.h b/src/hwinit/emc.h new file mode 100644 index 0000000..7a6405d --- /dev/null +++ b/src/hwinit/emc.h @@ -0,0 +1,667 @@ +/* +* arch/arm/mach-tegra/tegra21_emc.h +* +* Copyright (c) 2014-2015, NVIDIA CORPORATION. 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., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#ifndef _EMC_H_ +#define _EMC_H_ + +#define EMC_DBG 0x8 +#define EMC_CFG 0xC +#define EMC_CONFIG_SAMPLE_DELAY 0x5f0 +#define EMC_CFG_UPDATE 0x5f4 +#define EMC_ADR_CFG 0x10 +#define EMC_REFCTRL 0x20 +#define EMC_PIN 0x24 +#define EMC_TIMING_CONTROL 0x28 +#define EMC_RC 0x2c +#define EMC_RFC 0x30 +#define EMC_RFCPB 0x590 +#define EMC_RAS 0x34 +#define EMC_RP 0x38 +#define EMC_R2W 0x3c +#define EMC_W2R 0x40 +#define EMC_R2P 0x44 +#define EMC_W2P 0x48 +#define EMC_CCDMW 0x5c0 +#define EMC_RD_RCD 0x4c +#define EMC_WR_RCD 0x50 +#define EMC_RRD 0x54 +#define EMC_REXT 0x58 +#define EMC_WDV 0x5c +#define EMC_QUSE 0x60 +#define EMC_QRST 0x64 +#define EMC_ISSUE_QRST 0x428 +#define EMC_QSAFE 0x68 +#define EMC_RDV 0x6c +#define EMC_REFRESH 0x70 +#define EMC_BURST_REFRESH_NUM 0x74 +#define EMC_PDEX2WR 0x78 +#define EMC_PDEX2RD 0x7c +#define EMC_PDEX2CKE 0x118 +#define EMC_PCHG2PDEN 0x80 +#define EMC_ACT2PDEN 0x84 +#define EMC_AR2PDEN 0x88 +#define EMC_RW2PDEN 0x8c +#define EMC_CKE2PDEN 0x11c +#define EMC_TXSR 0x90 +#define EMC_TCKE 0x94 +#define EMC_TFAW 0x98 +#define EMC_TRPAB 0x9c +#define EMC_TCLKSTABLE 0xa0 +#define EMC_TCLKSTOP 0xa4 +#define EMC_TREFBW 0xa8 +#define EMC_TPPD 0xac +#define EMC_PDEX2MRR 0xb4 +#define EMC_ODT_WRITE 0xb0 +#define EMC_WEXT 0xb8 +#define EMC_RFC_SLR 0xc0 +#define EMC_MRS_WAIT_CNT2 0xc4 +#define EMC_MRS_WAIT_CNT 0xc8 +#define EMC_MRS 0xcc +#define EMC_EMRS 0xd0 +#define EMC_REF 0xd4 +#define EMC_PRE 0xd8 +#define EMC_NOP 0xdc +#define EMC_SELF_REF 0xe0 +#define EMC_DPD 0xe4 +#define EMC_MRW 0xe8 +#define EMC_MRR 0xec +#define EMC_CMDQ 0xf0 +#define EMC_MC2EMCQ 0xf4 +#define EMC_FBIO_SPARE 0x100 +#define EMC_FBIO_CFG5 0x104 +#define EMC_CFG_RSV 0x120 +#define EMC_ACPD_CONTROL 0x124 +#define EMC_MPC 0x128 +#define EMC_EMRS2 0x12c +#define EMC_EMRS3 0x130 +#define EMC_MRW2 0x134 +#define EMC_MRW3 0x138 +#define EMC_MRW4 0x13c +#define EMC_MRW5 0x4a0 +#define EMC_MRW6 0x4a4 +#define EMC_MRW7 0x4a8 +#define EMC_MRW8 0x4ac +#define EMC_MRW9 0x4b0 +#define EMC_MRW10 0x4b4 +#define EMC_MRW11 0x4b8 +#define EMC_MRW12 0x4bc +#define EMC_MRW13 0x4c0 +#define EMC_MRW14 0x4c4 +#define EMC_MRW15 0x4d0 +#define EMC_CFG_SYNC 0x4d4 +#define EMC_CLKEN_OVERRIDE 0x140 +#define EMC_R2R 0x144 +#define EMC_W2W 0x148 +#define EMC_EINPUT 0x14c +#define EMC_EINPUT_DURATION 0x150 +#define EMC_PUTERM_EXTRA 0x154 +#define EMC_TCKESR 0x158 +#define EMC_TPD 0x15c +#define EMC_STAT_CONTROL 0x160 +#define EMC_STAT_STATUS 0x164 +#define EMC_STAT_DRAM_CLOCK_LIMIT_LO 0x19c +#define EMC_STAT_DRAM_CLOCK_LIMIT_HI 0x1a0 +#define EMC_STAT_DRAM_CLOCKS_LO 0x1a4 +#define EMC_STAT_DRAM_CLOCKS_HI 0x1a8 +#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO 0x1ac +#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI 0x1b0 +#define EMC_STAT_DRAM_DEV0_READ_CNT_LO 0x1b4 +#define EMC_STAT_DRAM_DEV0_READ_CNT_HI 0x1b8 +#define EMC_STAT_DRAM_DEV0_READ8_CNT_LO 0x1bc +#define EMC_STAT_DRAM_DEV0_READ8_CNT_HI 0x1c0 +#define EMC_STAT_DRAM_DEV0_WRITE_CNT_LO 0x1c4 +#define EMC_STAT_DRAM_DEV0_WRITE_CNT_HI 0x1c8 +#define EMC_STAT_DRAM_DEV0_WRITE8_CNT_LO 0x1cc +#define EMC_STAT_DRAM_DEV0_WRITE8_CNT_HI 0x1d0 +#define EMC_STAT_DRAM_DEV0_REF_CNT_LO 0x1d4 +#define EMC_STAT_DRAM_DEV0_REF_CNT_HI 0x1d8 +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0x1dc +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0x1e0 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0x1e4 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0x1e8 +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0x1ec +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0x1f0 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0x1f4 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0x1f8 +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0x1fc +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0x200 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0x204 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0x208 +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0x20c +#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0x210 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0x214 +#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0x218 +#define EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_LO 0x21c +#define EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_HI 0x220 +#define EMC_STAT_DRAM_DEV0_DSR 0x224 +#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO 0x228 +#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI 0x22c +#define EMC_STAT_DRAM_DEV1_READ_CNT_LO 0x230 +#define EMC_STAT_DRAM_DEV1_READ_CNT_HI 0x234 +#define EMC_STAT_DRAM_DEV1_READ8_CNT_LO 0x238 +#define EMC_STAT_DRAM_DEV1_READ8_CNT_HI 0x23c +#define EMC_STAT_DRAM_DEV1_WRITE_CNT_LO 0x240 +#define EMC_STAT_DRAM_DEV1_WRITE_CNT_HI 0x244 +#define EMC_STAT_DRAM_DEV1_WRITE8_CNT_LO 0x248 +#define EMC_STAT_DRAM_DEV1_WRITE8_CNT_HI 0x24c +#define EMC_STAT_DRAM_DEV1_REF_CNT_LO 0x250 +#define EMC_STAT_DRAM_DEV1_REF_CNT_HI 0x254 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0x258 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0x25c +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0x260 +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0x264 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0x268 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0x26c +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0x270 +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0x274 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0x278 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0x27c +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0x280 +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0x284 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0x288 +#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0x28c +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0x290 +#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0x294 +#define EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_LO 0x298 +#define EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_HI 0x29c +#define EMC_STAT_DRAM_DEV1_DSR 0x2a0 +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0xc8c +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0xc90 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO 0xc94 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI 0xc98 +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0xc9c +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0xca0 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO 0xca4 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI 0xca8 +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0xcac +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0xcb0 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO 0xcb4 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI 0xcb8 +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0xcbc +#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0xcc0 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO 0xcc4 +#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI 0xcc8 +#define EMC_STAT_DRAM_IO_SR_CKE_EQ0_CLKS_LO 0xccc +#define EMC_STAT_DRAM_IO_SR_CKE_EQ0_CLKS_HI 0xcd0 +#define EMC_STAT_DRAM_IO_DSR 0xcd4 +#define EMC_AUTO_CAL_CONFIG 0x2a4 +#define EMC_AUTO_CAL_CONFIG2 0x458 +#define EMC_AUTO_CAL_CONFIG3 0x45c +#define EMC_AUTO_CAL_CONFIG4 0x5b0 +#define EMC_AUTO_CAL_CONFIG5 0x5b4 +#define EMC_AUTO_CAL_CONFIG6 0x5cc +#define EMC_AUTO_CAL_CONFIG7 0x574 +#define EMC_AUTO_CAL_CONFIG8 0x2dc +#define EMC_AUTO_CAL_VREF_SEL_0 0x2f8 +#define EMC_AUTO_CAL_VREF_SEL_1 0x300 +#define EMC_AUTO_CAL_INTERVAL 0x2a8 +#define EMC_AUTO_CAL_STATUS 0x2ac +#define EMC_AUTO_CAL_STATUS2 0x3d4 +#define EMC_AUTO_CAL_CHANNEL 0x464 +#define EMC_PMACRO_RX_TERM 0xc48 +#define EMC_PMACRO_DQ_TX_DRV 0xc70 +#define EMC_PMACRO_CA_TX_DRV 0xc74 +#define EMC_PMACRO_CMD_TX_DRV 0xc4c +#define EMC_PMACRO_AUTOCAL_CFG_0 0x700 +#define EMC_PMACRO_AUTOCAL_CFG_1 0x704 +#define EMC_PMACRO_AUTOCAL_CFG_2 0x708 +#define EMC_PMACRO_AUTOCAL_CFG_COMMON 0xc78 +#define EMC_PMACRO_ZCTRL 0xc44 +#define EMC_XM2COMPPADCTRL 0x30c +#define EMC_XM2COMPPADCTRL2 0x578 +#define EMC_XM2COMPPADCTRL3 0x2f4 +#define EMC_COMP_PAD_SW_CTRL 0x57c +#define EMC_REQ_CTRL 0x2b0 +#define EMC_EMC_STATUS 0x2b4 +#define EMC_CFG_2 0x2b8 +#define EMC_CFG_DIG_DLL 0x2bc +#define EMC_CFG_DIG_DLL_PERIOD 0x2c0 +#define EMC_DIG_DLL_STATUS 0x2c4 +#define EMC_CFG_DIG_DLL_1 0x2c8 +#define EMC_RDV_MASK 0x2cc +#define EMC_WDV_MASK 0x2d0 +#define EMC_RDV_EARLY_MASK 0x2d4 +#define EMC_RDV_EARLY 0x2d8 +#define EMC_WDV_CHK 0x4e0 +#define EMC_ZCAL_INTERVAL 0x2e0 +#define EMC_ZCAL_WAIT_CNT 0x2e4 +#define EMC_ZCAL_MRW_CMD 0x2e8 +#define EMC_ZQ_CAL 0x2ec +#define EMC_SCRATCH0 0x324 +#define EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE 0x3c8 +#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE 0x3cc +#define EMC_UNSTALL_RW_AFTER_CLKCHANGE 0x3d0 +#define EMC_FDPD_CTRL_CMD_NO_RAMP 0x4d8 +#define EMC_SEL_DPD_CTRL 0x3d8 +#define EMC_FDPD_CTRL_DQ 0x310 +#define EMC_FDPD_CTRL_CMD 0x314 +#define EMC_PRE_REFRESH_REQ_CNT 0x3dc +#define EMC_REFCTRL2 0x580 +#define EMC_FBIO_CFG7 0x584 +#define EMC_DATA_BRLSHFT_0 0x588 +#define EMC_DATA_BRLSHFT_1 0x58c +#define EMC_DQS_BRLSHFT_0 0x594 +#define EMC_DQS_BRLSHFT_1 0x598 +#define EMC_CMD_BRLSHFT_0 0x59c +#define EMC_CMD_BRLSHFT_1 0x5a0 +#define EMC_CMD_BRLSHFT_2 0x5a4 +#define EMC_CMD_BRLSHFT_3 0x5a8 +#define EMC_QUSE_BRLSHFT_0 0x5ac +#define EMC_QUSE_BRLSHFT_1 0x5b8 +#define EMC_QUSE_BRLSHFT_2 0x5bc +#define EMC_QUSE_BRLSHFT_3 0x5c4 +#define EMC_FBIO_CFG8 0x5c8 +#define EMC_CMD_MAPPING_CMD0_0 0x380 +#define EMC_CMD_MAPPING_CMD0_1 0x384 +#define EMC_CMD_MAPPING_CMD0_2 0x388 +#define EMC_CMD_MAPPING_CMD1_0 0x38c +#define EMC_CMD_MAPPING_CMD1_1 0x390 +#define EMC_CMD_MAPPING_CMD1_2 0x394 +#define EMC_CMD_MAPPING_CMD2_0 0x398 +#define EMC_CMD_MAPPING_CMD2_1 0x39c +#define EMC_CMD_MAPPING_CMD2_2 0x3a0 +#define EMC_CMD_MAPPING_CMD3_0 0x3a4 +#define EMC_CMD_MAPPING_CMD3_1 0x3a8 +#define EMC_CMD_MAPPING_CMD3_2 0x3ac +#define EMC_CMD_MAPPING_BYTE 0x3b0 +#define EMC_DYN_SELF_REF_CONTROL 0x3e0 +#define EMC_TXSRDLL 0x3e4 +#define EMC_CCFIFO_ADDR 0x3e8 +#define EMC_CCFIFO_DATA 0x3ec +#define EMC_CCFIFO_STATUS 0x3f0 +#define EMC_SWIZZLE_RANK0_BYTE0 0x404 +#define EMC_SWIZZLE_RANK0_BYTE1 0x408 +#define EMC_SWIZZLE_RANK0_BYTE2 0x40c +#define EMC_SWIZZLE_RANK0_BYTE3 0x410 +#define EMC_SWIZZLE_RANK1_BYTE0 0x418 +#define EMC_SWIZZLE_RANK1_BYTE1 0x41c +#define EMC_SWIZZLE_RANK1_BYTE2 0x420 +#define EMC_SWIZZLE_RANK1_BYTE3 0x424 +#define EMC_TR_TIMING_0 0x3b4 +#define EMC_TR_CTRL_0 0x3b8 +#define EMC_TR_CTRL_1 0x3bc +#define EMC_TR_DVFS 0x460 +#define EMC_SWITCH_BACK_CTRL 0x3c0 +#define EMC_TR_RDV 0x3c4 +#define EMC_TR_QPOP 0x3f4 +#define EMC_TR_RDV_MASK 0x3f8 +#define EMC_TR_QSAFE 0x3fc +#define EMC_TR_QRST 0x400 +#define EMC_IBDLY 0x468 +#define EMC_OBDLY 0x46c +#define EMC_TXDSRVTTGEN 0x480 +#define EMC_WE_DURATION 0x48c +#define EMC_WS_DURATION 0x490 +#define EMC_WEV 0x494 +#define EMC_WSV 0x498 +#define EMC_CFG_3 0x49c +#define EMC_CFG_PIPE_2 0x554 +#define EMC_CFG_PIPE_CLK 0x558 +#define EMC_CFG_PIPE_1 0x55c +#define EMC_CFG_PIPE 0x560 +#define EMC_QPOP 0x564 +#define EMC_QUSE_WIDTH 0x568 +#define EMC_PUTERM_WIDTH 0x56c +#define EMC_PROTOBIST_CONFIG_ADR_1 0x5d0 +#define EMC_PROTOBIST_CONFIG_ADR_2 0x5d4 +#define EMC_PROTOBIST_MISC 0x5d8 +#define EMC_PROTOBIST_WDATA_LOWER 0x5dc +#define EMC_PROTOBIST_WDATA_UPPER 0x5e0 +#define EMC_PROTOBIST_RDATA 0x5ec +#define EMC_DLL_CFG_0 0x5e4 +#define EMC_DLL_CFG_1 0x5e8 +#define EMC_TRAINING_CMD 0xe00 +#define EMC_TRAINING_CTRL 0xe04 +#define EMC_TRAINING_STATUS 0xe08 +#define EMC_TRAINING_QUSE_CORS_CTRL 0xe0c +#define EMC_TRAINING_QUSE_FINE_CTRL 0xe10 +#define EMC_TRAINING_QUSE_CTRL_MISC 0xe14 +#define EMC_TRAINING_WRITE_FINE_CTRL 0xe18 +#define EMC_TRAINING_WRITE_CTRL_MISC 0xe1c +#define EMC_TRAINING_WRITE_VREF_CTRL 0xe20 +#define EMC_TRAINING_READ_FINE_CTRL 0xe24 +#define EMC_TRAINING_READ_CTRL_MISC 0xe28 +#define EMC_TRAINING_READ_VREF_CTRL 0xe2c +#define EMC_TRAINING_CA_FINE_CTRL 0xe30 +#define EMC_TRAINING_CA_CTRL_MISC 0xe34 +#define EMC_TRAINING_CA_CTRL_MISC1 0xe38 +#define EMC_TRAINING_CA_VREF_CTRL 0xe3c +#define EMC_TRAINING_CA_TADR_CTRL 0xe40 +#define EMC_TRAINING_SETTLE 0xe44 +#define EMC_TRAINING_DEBUG_CTRL 0xe48 +#define EMC_TRAINING_DEBUG_DQ0 0xe4c +#define EMC_TRAINING_DEBUG_DQ1 0xe50 +#define EMC_TRAINING_DEBUG_DQ2 0xe54 +#define EMC_TRAINING_DEBUG_DQ3 0xe58 +#define EMC_TRAINING_MPC 0xe5c +#define EMC_TRAINING_PATRAM_CTRL 0xe60 +#define EMC_TRAINING_PATRAM_DQ 0xe64 +#define EMC_TRAINING_PATRAM_DMI 0xe68 +#define EMC_TRAINING_VREF_SETTLE 0xe6c +#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE0 0xe70 +#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE1 0xe74 +#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE2 0xe78 +#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE3 0xe7c +#define EMC_TRAINING_RW_EYE_CENTER_IB_MISC 0xe80 +#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE0 0xe84 +#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE1 0xe88 +#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE2 0xe8c +#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE3 0xe90 +#define EMC_TRAINING_RW_EYE_CENTER_OB_MISC 0xe94 +#define EMC_TRAINING_RW_OFFSET_IB_BYTE0 0xe98 +#define EMC_TRAINING_RW_OFFSET_IB_BYTE1 0xe9c +#define EMC_TRAINING_RW_OFFSET_IB_BYTE2 0xea0 +#define EMC_TRAINING_RW_OFFSET_IB_BYTE3 0xea4 +#define EMC_TRAINING_RW_OFFSET_IB_MISC 0xea8 +#define EMC_TRAINING_RW_OFFSET_OB_BYTE0 0xeac +#define EMC_TRAINING_RW_OFFSET_OB_BYTE1 0xeb0 +#define EMC_TRAINING_RW_OFFSET_OB_BYTE2 0xeb4 +#define EMC_TRAINING_RW_OFFSET_OB_BYTE3 0xeb8 +#define EMC_TRAINING_RW_OFFSET_OB_MISC 0xebc +#define EMC_TRAINING_OPT_CA_VREF 0xec0 +#define EMC_TRAINING_OPT_DQ_OB_VREF 0xec4 +#define EMC_TRAINING_OPT_DQ_IB_VREF_RANK0 0xec8 +#define EMC_TRAINING_OPT_DQ_IB_VREF_RANK1 0xecc +#define EMC_TRAINING_QUSE_VREF_CTRL 0xed0 +#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK0 0xed4 +#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK1 0xed8 +#define EMC_TRAINING_DRAMC_TIMING 0xedc +#define EMC_PMACRO_QUSE_DDLL_RANK0_0 0x600 +#define EMC_PMACRO_QUSE_DDLL_RANK0_1 0x604 +#define EMC_PMACRO_QUSE_DDLL_RANK0_2 0x608 +#define EMC_PMACRO_QUSE_DDLL_RANK0_3 0x60c +#define EMC_PMACRO_QUSE_DDLL_RANK0_4 0x610 +#define EMC_PMACRO_QUSE_DDLL_RANK0_5 0x614 +#define EMC_PMACRO_QUSE_DDLL_RANK1_0 0x620 +#define EMC_PMACRO_QUSE_DDLL_RANK1_1 0x624 +#define EMC_PMACRO_QUSE_DDLL_RANK1_2 0x628 +#define EMC_PMACRO_QUSE_DDLL_RANK1_3 0x62c +#define EMC_PMACRO_QUSE_DDLL_RANK1_4 0x630 +#define EMC_PMACRO_QUSE_DDLL_RANK1_5 0x634 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 0x640 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 0x644 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 0x648 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 0x64c +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4 0x650 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5 0x654 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 0x660 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 0x664 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 0x668 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 0x66c +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4 0x670 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5 0x674 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0 0x680 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1 0x684 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2 0x688 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3 0x68c +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4 0x690 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5 0x694 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0 0x6a0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1 0x6a4 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2 0x6a8 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3 0x6ac +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4 0x6b0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5 0x6b4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0 0x6c0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1 0x6c4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2 0x6c8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3 0x6cc +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_4 0x6d0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_5 0x6d4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0 0x6e0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1 0x6e4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2 0x6e8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3 0x6ec +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_4 0x6f0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_5 0x6f4 +#define EMC_PMACRO_TX_PWRD_0 0x720 +#define EMC_PMACRO_TX_PWRD_1 0x724 +#define EMC_PMACRO_TX_PWRD_2 0x728 +#define EMC_PMACRO_TX_PWRD_3 0x72c +#define EMC_PMACRO_TX_PWRD_4 0x730 +#define EMC_PMACRO_TX_PWRD_5 0x734 +#define EMC_PMACRO_TX_SEL_CLK_SRC_0 0x740 +#define EMC_PMACRO_TX_SEL_CLK_SRC_1 0x744 +#define EMC_PMACRO_TX_SEL_CLK_SRC_3 0x74c +#define EMC_PMACRO_TX_SEL_CLK_SRC_2 0x748 +#define EMC_PMACRO_TX_SEL_CLK_SRC_4 0x750 +#define EMC_PMACRO_TX_SEL_CLK_SRC_5 0x754 +#define EMC_PMACRO_DDLL_BYPASS 0x760 +#define EMC_PMACRO_DDLL_PWRD_0 0x770 +#define EMC_PMACRO_DDLL_PWRD_1 0x774 +#define EMC_PMACRO_DDLL_PWRD_2 0x778 +#define EMC_PMACRO_CMD_CTRL_0 0x780 +#define EMC_PMACRO_CMD_CTRL_1 0x784 +#define EMC_PMACRO_CMD_CTRL_2 0x788 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0 0x800 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1 0x804 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2 0x808 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3 0x80c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0 0x810 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1 0x814 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2 0x818 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3 0x81c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0 0x820 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1 0x824 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2 0x828 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3 0x82c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0 0x830 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1 0x834 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2 0x838 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3 0x83c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0 0x840 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1 0x844 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2 0x848 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3 0x84c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0 0x850 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1 0x854 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2 0x858 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3 0x85c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0 0x860 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1 0x864 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2 0x868 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3 0x86c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0 0x870 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1 0x874 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2 0x878 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3 0x87c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0 0x880 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1 0x884 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2 0x888 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3 0x88c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0 0x890 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1 0x894 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2 0x898 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3 0x89c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0 0x8a0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1 0x8a4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2 0x8a8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3 0x8ac +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0 0x8b0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1 0x8b4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2 0x8b8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3 0x8bc +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0 0x900 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1 0x904 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2 0x908 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3 0x90c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0 0x910 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1 0x914 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2 0x918 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3 0x91c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0 0x920 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1 0x924 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2 0x928 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3 0x92c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0 0x930 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1 0x934 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2 0x938 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3 0x93c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0 0x940 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1 0x944 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2 0x948 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3 0x94c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0 0x950 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1 0x954 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2 0x958 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3 0x95c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0 0x960 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1 0x964 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2 0x968 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3 0x96c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0 0x970 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1 0x974 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2 0x978 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3 0x97c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0 0x980 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1 0x984 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2 0x988 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3 0x98c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0 0x990 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1 0x994 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2 0x998 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3 0x99c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0 0x9a0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1 0x9a4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2 0x9a8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3 0x9ac +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0 0x9b0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1 0x9b4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2 0x9b8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3 0x9bc +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0 0xa00 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1 0xa04 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2 0xa08 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0 0xa10 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1 0xa14 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2 0xa18 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0 0xa20 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1 0xa24 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2 0xa28 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0 0xa30 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1 0xa34 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2 0xa38 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0 0xa40 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1 0xa44 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2 0xa48 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0 0xa50 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1 0xa54 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2 0xa58 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0 0xa60 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1 0xa64 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2 0xa68 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0 0xa70 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1 0xa74 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2 0xa78 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_0 0xa80 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_1 0xa84 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_2 0xa88 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_0 0xa90 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_1 0xa94 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_2 0xa98 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_0 0xaa0 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_1 0xaa4 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_2 0xaa8 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_0 0xab0 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_1 0xab4 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_2 0xab8 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0 0xb00 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1 0xb04 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2 0xb08 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0 0xb10 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1 0xb14 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2 0xb18 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0 0xb20 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1 0xb24 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2 0xb28 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0 0xb30 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1 0xb34 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2 0xb38 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0 0xb40 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1 0xb44 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2 0xb48 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0 0xb50 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1 0xb54 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2 0xb58 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0 0xb60 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1 0xb64 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2 0xb68 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0 0xb70 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1 0xb74 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2 0xb78 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_0 0xb80 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_1 0xb84 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_2 0xb88 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_0 0xb90 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_1 0xb94 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_2 0xb98 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_0 0xba0 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_1 0xba4 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_2 0xba8 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_0 0xbb0 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_1 0xbb4 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_2 0xbb8 +#define EMC_PMACRO_IB_VREF_DQ_0 0xbe0 +#define EMC_PMACRO_IB_VREF_DQ_1 0xbe4 +#define EMC_PMACRO_IB_VREF_DQ_2 0xbe8 +#define EMC_PMACRO_IB_VREF_DQS_0 0xbf0 +#define EMC_PMACRO_IB_VREF_DQS_1 0xbf4 +#define EMC_PMACRO_IB_VREF_DQS_2 0xbf8 +#define EMC_PMACRO_IB_RXRT 0xcf4 +#define EMC_PMACRO_DDLL_LONG_CMD_0 0xc00 +#define EMC_PMACRO_DDLL_LONG_CMD_1 0xc04 +#define EMC_PMACRO_DDLL_LONG_CMD_2 0xc08 +#define EMC_PMACRO_DDLL_LONG_CMD_3 0xc0c +#define EMC_PMACRO_DDLL_LONG_CMD_4 0xc10 +#define EMC_PMACRO_DDLL_LONG_CMD_5 0xc14 +#define EMC_PMACRO_DDLL_SHORT_CMD_0 0xc20 +#define EMC_PMACRO_DDLL_SHORT_CMD_1 0xc24 +#define EMC_PMACRO_DDLL_SHORT_CMD_2 0xc28 +#define EMC_PMACRO_CFG_PM_GLOBAL_0 0xc30 +#define EMC_PMACRO_VTTGEN_CTRL_0 0xc34 +#define EMC_PMACRO_VTTGEN_CTRL_1 0xc38 +#define EMC_PMACRO_VTTGEN_CTRL_2 0xcf0 +#define EMC_PMACRO_BG_BIAS_CTRL_0 0xc3c +#define EMC_PMACRO_PAD_CFG_CTRL 0xc40 +#define EMC_PMACRO_CMD_PAD_RX_CTRL 0xc50 +#define EMC_PMACRO_DATA_PAD_RX_CTRL 0xc54 +#define EMC_PMACRO_CMD_RX_TERM_MODE 0xc58 +#define EMC_PMACRO_DATA_RX_TERM_MODE 0xc5c +#define EMC_PMACRO_CMD_PAD_TX_CTRL 0xc60 +#define EMC_PMACRO_DATA_PAD_TX_CTRL 0xc64 +#define EMC_PMACRO_COMMON_PAD_TX_CTRL 0xc68 +#define EMC_PMACRO_BRICK_MAPPING_0 0xc80 +#define EMC_PMACRO_BRICK_MAPPING_1 0xc84 +#define EMC_PMACRO_BRICK_MAPPING_2 0xc88 +#define EMC_PMACRO_DDLLCAL_CAL 0xce0 +#define EMC_PMACRO_DDLL_OFFSET 0xce4 +#define EMC_PMACRO_DDLL_PERIODIC_OFFSET 0xce8 +#define EMC_PMACRO_BRICK_CTRL_RFU1 0x330 +#define EMC_PMACRO_BRICK_CTRL_RFU2 0x334 +#define EMC_PMACRO_CMD_BRICK_CTRL_FDPD 0x318 +#define EMC_PMACRO_DATA_BRICK_CTRL_FDPD 0x31c +#define EMC_PMACRO_TRAINING_CTRL_0 0xcf8 +#define EMC_PMACRO_TRAINING_CTRL_1 0xcfc +#define EMC_PMC_SCRATCH1 0x440 +#define EMC_PMC_SCRATCH2 0x444 +#define EMC_PMC_SCRATCH3 0x448 + +#endif diff --git a/src/hwinit/ff.c b/src/hwinit/ff.c new file mode 100644 index 0000000..d29601d --- /dev/null +++ b/src/hwinit/ff.c @@ -0,0 +1,6555 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem Module R0.13a / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if FF_DEFINED != 89352 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Character code support macros */ +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') +#define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) + + +/* Additional file attribute bits for internal use */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Additional file access control and file status flags for internal use */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Name status flags in fn[11] */ +#define NSFLAG 11 /* Index of the name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* Limits and boundaries */ +#define MAX_DIR 0x200000 /* Max size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ + + +/* FatFs refers the FAT structure as simple byte array instead of structure member +/ because the C structure is not binary compatible between different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* WindowsNT error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* Filesystem type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ +#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */ +#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + + +/* Post process on fatal error in the file operations */ +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Re-entrancy related */ +#if FF_FS_REENTRANT +#if FF_USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define LEAVE_FF(fs, res) return res +#endif + + +/* Definitions of volume - physical location conversion */ +#if FF_MULTI_PARTITION +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#else +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#endif + + +/* Definitions of sector size */ +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if FF_MAX_SS == FF_MIN_SS +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if FF_FS_NORTC == 1 +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 +#error Invalid FF_FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if FF_FS_LOCK != 0 +#if FF_FS_READONLY +#error FF_FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, containing directory (0:root) */ + DWORD ofs; /* Object ID 3, offset in the directory */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* SBCS up-case tables (\x80-\xFF) */ +#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + + +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */ +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00} +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00} +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE} +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00} + + +/* Macros for table definitions */ +#define MERGE_2STR(a, b) a ## b +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ +/* Remark: Variables defined here without initial value shall be guaranteed +/ zero/null at start-up. If not, the linker option or start-up routine is +/ not compliance with C standard. */ + +/*--------------------------------*/ +/* File/Volume controls */ +/*--------------------------------*/ + +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 +#error Wrong FF_VOLUMES setting +#endif +static FATFS *FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if FF_FS_RPATH != 0 && FF_VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if FF_FS_LOCK != 0 +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ +#endif + + + +/*--------------------------------*/ +/* LFN/Directory working buffer */ +/*--------------------------------*/ + +#if FF_USE_LFN == 0 /* Non-LFN configuration */ +#if FF_FS_EXFAT +#error LFN must be enabled when enable exFAT +#endif +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#else /* LFN configurations */ +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 +#error Wrong setting of FF_MAX_LFN +#endif +#if FF_LFN_BUF < 12 || FF_SFN_BUF < 12 || FF_LFN_BUF < FF_SFN_BUF +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF +#endif +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 2 +#error Wrong setting of FF_LFN_UNICODE +#endif +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */ +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */ + +#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ +#if FF_FS_EXFAT +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */ +#endif +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } +#define MAX_MALLOC 0x8000 + +#else +#error Wrong setting of FF_USE_LFN + +#endif /* FF_USE_LFN == 1 */ +#endif /* FF_USE_LFN == 0 */ + + + +/*--------------------------------*/ +/* Code conversion tables */ +/*--------------------------------*/ + +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ +#define CODEPAGE CodePage +static WORD CodePage; /* Current code page */ +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ +static const BYTE Ct437[] = TBL_CT437; +static const BYTE Ct720[] = TBL_CT720; +static const BYTE Ct737[] = TBL_CT737; +static const BYTE Ct771[] = TBL_CT771; +static const BYTE Ct775[] = TBL_CT775; +static const BYTE Ct850[] = TBL_CT850; +static const BYTE Ct852[] = TBL_CT852; +static const BYTE Ct855[] = TBL_CT855; +static const BYTE Ct857[] = TBL_CT857; +static const BYTE Ct860[] = TBL_CT860; +static const BYTE Ct861[] = TBL_CT861; +static const BYTE Ct862[] = TBL_CT862; +static const BYTE Ct863[] = TBL_CT863; +static const BYTE Ct864[] = TBL_CT864; +static const BYTE Ct865[] = TBL_CT865; +static const BYTE Ct866[] = TBL_CT866; +static const BYTE Ct869[] = TBL_CT869; +static const BYTE Dc932[] = TBL_DC932; +static const BYTE Dc936[] = TBL_DC936; +static const BYTE Dc949[] = TBL_DC949; +static const BYTE Dc950[] = TBL_DC950; + +#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); + +#else /* Static code page configuration (DBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if FF_FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !FF_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if FF_FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !FF_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt != 0) { + do { + *d++ = *s++; + } while (--cnt); + } +} + + +/* Fill memory block */ +static +void mem_set (void* dst, int val, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + + do { + *d++ = (BYTE)val; + } while (--cnt); +} + + +/* Compare memory block */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:different */ +{ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */ +{ + while (*str && *str != chr) str++; + return *str; +} + + +/* Test if the character is DBC 1st byte */ +static +int dbc_1st (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */ + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +/* Test if the character is DBC 2nd byte */ +static +int dbc_2nd (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */ + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */ + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +#if FF_USE_LFN + +/* Get a character from TCHAR string in defined API encodeing */ +static +DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */ + const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ +) +{ + DWORD uc; + const TCHAR *p = *str; + +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + WCHAR wc; + + uc = *p++; + if (IsSurrogate(uc)) { /* Surrogate? */ + wc = *p++; /* Get low surrogate */ + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */ + uc = uc << 16 | wc; + } + +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + BYTE b; + int nf; + + uc = (BYTE)*p++; /* Get a byte */ + if (uc & 0x80) { /* Multiple byte code? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + uc &= 0x1F; nf = 1; + } else { + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else { + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; + } + } + } + do { /* Get trailing bytes */ + b = (BYTE)*p++; + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */ + uc = uc << 6 | (b & 0x3F); + } while (--nf != 0); + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x10000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + } + +#else /* ANSI/OEM input */ + BYTE b; + WCHAR wc; + + wc = (BYTE)*p++; /* Get a byte */ + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ + b = (BYTE)*p++; /* Get 2nd byte */ + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ + wc = (wc << 8) + b; /* Make a DBC */ + } + if (wc != 0) { + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ + } + uc = wc; + +#endif + *str = p; /* Next read pointer */ + return uc; +} + + +/* Output a TCHAR string in defined API encoding */ +static +BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ + DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */ + TCHAR* buf, /* Output buffer */ + UINT szb /* Size of the buffer */ +) +{ +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ + WCHAR hs, wc; + + hs = (WCHAR)(chr >> 16); + wc = (WCHAR)chr; + if (hs == 0) { /* Single encoding unit? */ + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */ + *buf = wc; + return 1; + } + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */ + *buf++ = hs; + *buf++ = wc; + return 2; + +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ + DWORD hc; + + if (chr < 0x80) { /* Single byte code? */ + if (szb < 1) return 0; /* Buffer overflow? */ + *buf = (TCHAR)chr; + return 1; + } + if (chr < 0x800) { /* 2-byte sequence? */ + if (szb < 2) return 0; /* Buffer overflow? */ + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 2; + } + if (chr < 0x10000) { /* 3-byte sequence? */ + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 3; + } + /* 4-byte sequence */ + if (szb < 4) return 0; /* Buffer overflow? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 4; + +#else /* ANSI/OEM output */ + WCHAR wc; + + wc = ff_uni2oem(chr, CODEPAGE); + if (wc >= 0x100) { /* Is this a DBC? */ + if (szb < 2) return 0; + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + return 2; + } + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ + *buf++ = (TCHAR)wc; /* Store the character */ + return 1; +#endif +} +#endif /* FF_USE_LFN */ + + +#if FF_FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static +int lock_fs ( /* 1:Ok, 0:timeout */ + FATFS* fs /* Filesystem object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS* fs, /* Filesystem object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if FF_FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */ +) +{ + UINT i, be; + + /* Search open object table for the object */ + be = 0; + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == FF_FS_LOCK) { /* The object has not been opened */ + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */ + } + + /* The object was opened. Reject any open against writing file and all write mode open */ + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + return (i == FF_FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } + + if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; /* Index number origin from 1 */ +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* FF_FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the filesystem object */ +/*-----------------------------------------------------------------------*/ +#if !FF_FS_READONLY +static +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Is the disk access window dirty */ + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write back the window */ + fs->wflag = 0; /* Clear window dirty flag */ + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */ + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ + } + } else { + res = FR_DISK_ERR; + } + } + return res; +} +#endif + + +static +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs, /* Filesystem object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !FF_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->pdrv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if read data is not valid */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize filesystem and data on the storage */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ + /* Create FSInfo structure */ + mem_set(fs->win, 0, SS(fs)); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* Write it into the FSInfo sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->pdrv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the lower layer */ + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get physical sector number from cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* Filesystem object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; /* Cluster number is origin from 2 */ + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */ + return fs->database + fs->csize * clst; /* Start sector number of the cluster */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */ + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */ + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */ + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */ + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ + break; +#if FF_FS_EXFAT + case FS_EXFAT : + if (obj->objsize != 0) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */ + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */ + break; + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (obj->n_frag != 0) { /* Is it on the growing edge? */ + val = 0x7FFFFFFF; /* Generate EOC */ + } else { + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + } + break; + } + } + /* go to default */ +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding filesystem object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Put 1st byte */ + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Put 2nd byte */ + fs->wflag = 1; + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ + fs->wflag = 1; + break; + + case FS_FAT32 : +#if FF_FS_EXFAT + case FS_EXFAT : +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_FS_EXFAT && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*--------------------------------------*/ +/* Find a contiguous free cluster block */ +/*--------------------------------------*/ + +static +DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = SS(fs); + } + if (bv == 0) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ + } else { + scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm != 0); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*----------------------------------------*/ +/* Set/Clear a block of allocation bitmap */ +/*----------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Fill the first fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_first_frag ( + FFOBJID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + + if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + + +/*---------------------------------------------*/ +/* Fill the last fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_last_frag ( + FFOBJID* obj, /* Pointer to the corresponding object */ + DWORD lcl, /* Last cluster of the fragment */ + DWORD term /* Value to set the last FAT entry */ +) +{ + FRESULT res; + + + while (obj->n_frag > 0) { /* Create the chain of last fragment */ + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); + if (res != FR_OK) return res; + obj->n_frag--; + } + return FR_OK; +} + +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if FF_FS_EXFAT || FF_USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if FF_USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if FF_FS_EXFAT || FF_USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } +#endif +#if FF_USE_TRIM + rt[0] = clst2sect(fs, scl); /* Start of data area freed */ + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area freed */ + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform device the data in the block is no longer needed */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if FF_FS_EXFAT + /* Some post processes for chain status */ + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Has the entire chain been removed? */ + obj->stat = 0; /* Change the chain status 'initial' */ + } else { + if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */ + clst = obj->sclust; /* Follow the chain to check if it gets contiguous */ + while (clst != pclst) { + nxt = get_fat(obj, clst); + if (nxt < 2) return FR_INT_ERR; + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; + if (nxt != clst + 1) break; /* Not contiguous? */ + clst++; + } + if (clst == pclst) { /* Has the chain got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Suggested cluster to start to find */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch a chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Test for insanity */ + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; /* Cluster to start to find */ + } + if (fs->free_clst == 0) return 0; /* No free cluster */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous' */ + } else { /* It is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + if (obj->stat != 2) { /* Is the file non-contiguous? */ + if (ncl == clst + 1) { /* Is the cluster next to previous one? */ + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */ + } else { /* New fragment */ + if (obj->n_frag == 0) obj->n_frag = 1; + res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */ + if (res == FR_OK) obj->n_frag = 1; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + ncl = 0; + if (scl == clst) { /* Stretching an existing chain? */ + ncl = scl + 1; /* Test if next cluster is free */ + if (ncl >= fs->n_fatent) ncl = 2; + cs = get_fat(obj, ncl); /* Get next cluster status */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (cs != 0) { /* Not free? */ + cs = fs->last_clst; /* Start at suggested cluster if it is valid */ + if (cs >= 2 && cs < fs->n_fatent) scl = cs; + ncl = 0; + } + } + if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster found? */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster? */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (ncl == scl) return 0; /* No free cluster found? */ + } + } + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* FF_USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Fill a cluster with zeros */ +/*-----------------------------------------------------------------------*/ + +#if !FF_FS_READONLY +static +FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS *fs, /* Filesystem object */ + DWORD clst /* Directory table to clear */ +) +{ + DWORD sect; + UINT n, szb; + BYTE *ibuf; + + + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + sect = clst2sect(fs, clst); /* Top of the cluster */ + fs->winsect = sect; /* Set window to top of the cluster */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ +#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ + /* Allocate a temporary buffer */ + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs); szb > SS(fs) && !(ibuf = ff_memalloc(szb)); szb /= 2) ; + if (szb > SS(fs)) { /* Buffer allocated? */ + mem_set(ibuf, 0, szb); + szb /= SS(fs); /* Bytes -> Sectors */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + ff_memfree(ibuf); + } else +#endif + { + ibuf = fs->win; szb = 1; /* Use window buffer (single-sector writes may take a time) */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + } + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; +} +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory on the FAT volume) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clst2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (dp->sect == 0) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; + + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (dp->sect == 0 || ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (dp->clust == 0) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* It reached end of dynamic table */ +#if !FF_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */ + if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */ +#else + if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clst2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if FF_FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !FF_FS_READONLY +static +void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if FF_USE_LFN +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ +static +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ +static +int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= FF_MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !FF_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ +static +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LFN */ + + + +#if FF_USE_LFN && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (dbc_1st(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* FF_USE_LFN && !FF_FS_READONLY */ + + + +#if FF_USE_LFN +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do { + sum = (sum >> 1) + (sum << 7) + *dir++; + } while (--n); + return sum; +} + +#endif /* FF_USE_LFN */ + + + +#if FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly entry block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be upper-case converted */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !FF_FS_READONLY && FF_USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Byte to be calculated */ + DWORD sum /* Previous sum */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xfileinfo ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + WCHAR wc, hs; + UINT di, si, nc; + + /* Get file name from the entry block */ + si = SZDIRE * 2; /* 1st C1 entry */ + nc = hs = di = 0; + while (nc < dirb[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not have SFN */ + + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Reading direcotry object pointing top of the entry block to load */ +) +{ + FRESULT res; + UINT i, sz_ent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; + + /* Load C1 entries */ + i = 2 * SZDIRE; /* C1 offset to load */ + do { + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; /* Invalid order */ + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + } while ((i += SZDIRE) < sz_ent); + + /* Sanity check (do it for only accessible object) */ + if (i <= MAXDIRB(FF_MAX_LFN)) { + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + } + return FR_OK; +} + + +/*------------------------------------------------------------------*/ +/* exFAT: Initialize object allocation info with loaded entry block */ +/*------------------------------------------------------------------*/ + +static +void init_alloc_info ( + FATFS* fs, /* Filesystem object */ + FFOBJID* obj /* Object allocation information to be initialized */ +) +{ + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */ + obj->n_frag = 0; /* No last fragment info */ +} + + + +#if !FF_FS_READONLY || FF_FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_xdir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const FFOBJID* obj /* Object with its containing directory information */ +) +{ + FRESULT res; + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->obj.n_frag = 0; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !FF_FS_READONLY +/*----------------------------------------*/ +/* exFAT: Store the directory entry block */ +/*----------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the direcotry entry block to the directory */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the object name */ +) +{ + UINT i; + BYTE nc1, nlen; + WCHAR wc; + + + /* Create 85,C0 entry */ + mem_set(dirb, 0, 2 * SZDIRE); + dirb[0 * SZDIRE + XDIR_Type] = 0x85; /* 85 entry */ + dirb[1 * SZDIRE + XDIR_Type] = 0xC0; /* C0 entry */ + + /* Create C1 entries */ + i = SZDIRE * 2; /* Top of C1 entries */ + nlen = nc1 = 0; wc = 1; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ + st_word(dirb + i, wc); /* Store it */ + i += 2; + } while (i % SZDIRE != 0); + nc1++; + } while (lfn[nlen]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nlen; /* Set name length */ + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_EXFAT */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +#define dir_read_file(dp) dir_read(dp, 0) +#define dir_read_label(dp) dir_read(dp, 1) + +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if FF_USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { + res = FR_NO_FILE; break; /* Reached to end of the directory */ + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (FF_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if FF_USE_LFN /* LFN configuration */ + if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if FF_USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = dir_read_file(dp)) == FR_OK) { /* Read an item */ +#if FF_MAX_LFN < 255 + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ +#endif + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT/FAT32 volume */ +#if FF_USE_LFN + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if FF_USE_LFN /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + + if (dp->obj.stat & 4) { /* Has the directory been stretched? */ + dp->obj.stat &= ~4; + res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */ + if (res != FR_OK) return res; + if (dp->obj.sclust != 0) { /* Is it a sub directory? */ + DIR dj; + + res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */ + if (res != FR_OK) return res; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT/FAT32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if FF_USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ + } else { /* On the FAT/FAT32 volume */ + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static +void get_fileinfo ( + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT si, di; +#if FF_USE_LFN + WCHAR wc, hs; + FATFS *fs = dp->obj.fs; +#else + TCHAR c; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */ + +#if FF_USE_LFN /* LFN configuration */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xfileinfo(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT/FAT32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + si = di = hs = 0; + while (fs->lfnbuf[si] != 0) { + wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */ + } + } + + si = di = 0; + while (si < 11) { /* Get SFN from SFN entry */ + wc = dp->dir[si++]; /* Get a char */ + if (wc == ' ') continue; /* Skip padding spaces */ + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */ + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */ +#if FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ + wc = wc << 8 | dp->dir[si++]; + } + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ + if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in UTF-16 or UTF-8 */ + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ + di += wc; +#else /* ANSI/OEM output */ + fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ +#endif + } + fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ + + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */ + if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */ + fno->fname[di++] = '?'; + } else { + for (si = di = 0; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */ + wc = (WCHAR)fno->altname[si]; + if (IsUpper(wc) && (dp->dir[DIR_NTres] & ((si >= 9) ? NS_EXT : NS_BODY))) wc += 0x20; + fno->fname[di] = (TCHAR)wc; + } + } + fno->fname[di] = 0; /* Terminate the LFN */ + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */ + } + +#else /* Non-LFN configuration */ + si = di = 0; + while (si < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[si++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ + if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ + fno->fname[di++] = c; + } + fno->fname[di] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + + +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_achar ( /* Get a character and advances ptr */ + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ +) +{ + DWORD chr; + + +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ + chr = tchar2uni(ptr); + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */ + chr = ff_wtoupper(chr); + +#else /* ANSI/OEM input */ + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#if FF_CODE_PAGE == 0 + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#elif FF_CODE_PAGE < 900 + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; + } +#endif + +#endif + return chr; +} + + +static +int pattern_matching ( /* 0:not matched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + DWORD pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (*pat == 0 && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard block */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if FF_USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR wc, *lfn; + DWORD uc; + UINT i, ni, si, di; + const TCHAR *p; + + + /* Create LFN into LFN working buffer */ + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; + for (;;) { + uc = tchar2uni(&p); /* Get a character */ + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ + wc = (WCHAR)uc; + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ + lfn[di++] = wc; /* Store the Unicode character */ + } + while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + *path = p; /* Return pointer to the next segment */ + cf = (wc < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + +#if FF_FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + } + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + wc = lfn[di - 1]; + if (wc != ' ' && wc != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created into the working buffer */ + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ + + /* Create SFN in directory form */ + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ + + mem_set(dp->fn, ' ', 11); + i = b = 0; ni = 8; + for (;;) { + wc = lfn[si++]; /* Get an LFN character */ + if (wc == 0) break; /* Break on end of the LFN */ + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */ + cf |= NS_LOSS | NS_LFN; + continue; + } + + if (i >= ni || si == di) { /* End of field? */ + if (ni == 11) { /* Name extension overflow? */ + cf |= NS_LOSS | NS_LFN; + break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */ + if (si > di) break; /* No name extension? */ + si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */ + continue; + } + + if (wc >= 0x80) { /* Is this a non-ASCII character? */ + cf |= NS_LFN; /* LFN entry needs to be created */ +#if FF_CODE_PAGE == 0 + if (ExCvt) { /* At SBCS */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ + } else { /* At DBCS */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ + } +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ +#else /* DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ +#endif + } + + if (wc >= 0x100) { /* Is this a DBC? */ + if (i >= ni - 1) { /* Field overflow? */ + cf |= NS_LOSS | NS_LFN; + i = ni; continue; /* Next field */ + } + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ + } else { /* SBC */ + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(wc)) { /* ASCII upper case? */ + b |= 2; + } + if (IsLower(wc)) { /* ASCII lower case? */ + b |= 1; wc -= 0x20; + } + } + } + dp->fn[i++] = (BYTE)wc; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; /* Shift capital flags if no extension */ + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */ + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ + + return FR_OK; + + +#else /* FF_USE_LFN : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if FF_FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; /* Get a byte */ + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or field overflow? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */ + i = 8; ni = 11; /* Enter file extension field */ + continue; + } +#if FF_CODE_PAGE == 0 + if (ExCvt && c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#elif FF_CODE_PAGE < 900 + if (c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#endif + if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* FF_USE_LFN */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + FATFS *fs = dp->obj.fs; + + +#if FF_FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + dp->obj.sclust = fs->cdir; /* Start from current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + dp->obj.sclust = 0; /* Start from root directory */ + } +#if FF_FS_EXFAT + dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */ +#if FF_FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */ + DIR dj; + + dp->obj.c_scl = fs->cdc_scl; + dp->obj.c_size = fs->cdc_size; + dp->obj.c_ofs = fs->cdc_ofs; + res = load_obj_xdir(&dj, &dp->obj); + if (res != FR_OK) return res; + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + dp->obj.c_scl = dp->obj.sclust; + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Open next directory */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if FF_STR_VOLUME_ID /* Find string drive id */ + static const char* const volid[] = {FF_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path != 0) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (FF_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find a colon in the path */ + if (*tt == ':') { /* If a colon is exist in the path name */ + tp = *path; + i = *tp++; + if (IsDigit(i) && tp == tt) { /* Is there a numeric drive id + colon? */ + if ((i -= '0') < FF_VOLUMES) { /* If drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if FF_STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = volid[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */ + if (i < FF_VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + } else { /* No volume id and use default drive */ +#if FF_FS_RPATH != 0 && FF_VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT VBR */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ + FATFS* fs, /* Filesystem object */ + DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ + + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed here even if the sector size is >512) */ + +#if FF_FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ +#endif + if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */ + if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */ + if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */ + } + return 2; /* Valid BS but not FAT */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Determine logical drive number and mount the volume if needed */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found filesystem object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the filesystem object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the filesystem object */ + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */ +#if FF_FS_REENTRANT + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ +#endif + *rfs = fs; /* Return pointer to the filesystem object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type != 0) { /* If the volume has been mounted */ + stat = disk_status(fs->pdrv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The filesystem object is valid */ + } + } + + /* The filesystem object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */ + + fs->fs_type = 0; /* Clear the filesystem object */ + fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; + } +#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + + /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i != 0) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4); + } + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found (bsect). Following code initializes the filesystem object */ + +#if FF_FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + } + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clst2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; +#if !FF_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* FF_FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = 0; + if (nclst <= MAX_FAT32) fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + if (fmt == 0) return FR_NO_FILESYSTEM; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !FF_FS_READONLY + /* Get FSInfo if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (FF_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (FF_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (FF_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ +#endif /* !FF_FS_READONLY */ + } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* Volume mount ID */ +#if FF_USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if FF_FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */ +#endif +#endif +#if FF_FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */ + FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */ +) +{ + FRESULT res = FR_INVALID_OBJECT; + + + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */ +#if FF_FS_REENTRANT + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } else { + unlock_fs(obj->fs, FR_OK); + } + } else { + res = FR_TIMEOUT; + } +#else + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } +#endif + } + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */ + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if FF_FS_LOCK != 0 + clear_lock(cfs); +#endif +#if FF_FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if FF_FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !FF_FS_READONLY + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; + res = find_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !FF_FS_READONLY /* Read/Write configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if FF_FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */ + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */ +#if FF_FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + } + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object with the same name is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + init_alloc_info(fs, &fp->obj); + /* Set directory entry block initial state */ + mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ + fs->dirbuf[XDIR_Attr] = AM_ARC; + st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Set directory entry initial state */ + cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ + st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + if (cl != 0) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Is the object exsiting? */ + if (dj.obj.attr & AM_DIR) { /* File open against a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */ + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if FF_FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */ + if (fp->obj.lockid == 0) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* Is it a directory? */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + init_alloc_info(fs, &fp->obj); + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if FF_USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !FF_FS_READONLY +#if !FF_FS_TINY + mem_set(fp->buf, 0, FF_MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clst2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !FF_FS_TINY + if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if FF_FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !FF_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */ + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if FF_FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if FF_FS_MINIMIZE <= 2 +#if FF_FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if FF_FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !FF_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */ + if (res == FR_OK) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } + if (res == FR_OK) { + DIR dj; + DEF_NAMBUF + + INIT_NAMBUF(fs); + res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !FF_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */ +#else + fp->obj.fs = 0; /* Invalidate file object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if FF_FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if FF_VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(fs, res); +} + + +#if FF_FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMBUF + + + *buff = 0; + /* Get logical drive */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + i = len; /* Bottom of buffer (directory stack base) */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read_file(&dj); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; + } + } + tp = buff; + if (res == FR_OK) { +#if FF_VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_FS_RPATH >= 2 */ +#endif /* FF_FS_RPATH >= 1 */ + + + +#if FF_FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File Read/Write Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; +#if FF_USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) res = (FRESULT)fp->err; +#if FF_FS_EXFAT && !FF_FS_READONLY + if (res == FR_OK && fs->fs_type == FS_EXFAT) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } +#endif + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl != 0) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs > 0) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clst2sect(fs, fp->clust); + if (dsc == 0) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs > 0) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !FF_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !FF_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clst2sect(fs, clst); /* Current sector */ + if (nsect == 0) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dp->obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */ + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Get object allocation info */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dp->obj.id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + if (dp->obj.sclust != 0) { + dp->obj.lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->obj.lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* Decrement sub-directory open counter */ + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */ +#else + dp->obj.fs = 0; /* Invalidate directory object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = dir_read_file(dp); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if FF_USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if FF_USE_LFN && FF_USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* FF_USE_FIND */ + + + +#if FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + FFOBJID obj; + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full FAT scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Scan FAT to obtain number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; /* Number of clusters */ + sect = fs->database; /* Assuming bitmap starts at cluster 2 */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of bits with zero in the bitmap */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Scan WORD/DWORD FAT entries */ + clst = fs->n_fatent; /* Number of entries */ + sect = fs->fatbase; /* Top of the FAT */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of entries with zero in the FAT */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(fs->win + i) == 0) nfree++; + i += 2; + } else { + if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++; + i += 4; + } + i %= SS(fs); + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */ + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */ + fp->flag |= FA_MODIFIED; +#if !FF_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if FF_FS_EXFAT + FFOBJID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if FF_FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if FF_FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + init_alloc_info(fs, &obj); + dclst = obj.sclust; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ +#if FF_FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = dir_read_file(&sdj); /* Test if the directory is empty */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */ +#if FF_FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE *dir; + DWORD dcl, pcl, tm; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); + if (res == FR_OK) { /* Initialize the new directory table */ + res = dir_clear(fs, dcl); /* Clean up the new table */ + if (res == FR_OK && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT)) { /* Create dot entries (FAT only) */ + dir = fs->win; + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + st_clust(fs, dir + SZDIRE, pcl); + fs->wflag = 1; + } + } + if (res == FR_OK) { + res = dir_register(&dj); /* Register the object to the directoy */ + } + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } else { + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Snip the drive number of new name off */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + res = chk_lock(&djo, 2); + } +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ +/* Start of critical section where an interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT/FAT32 volume */ + mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy directory entry of the object except name */ + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + dir[DIR_Attr] = buf[DIR_Attr]; + if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clst2sect(fs, ld_clust(fs, dir)); + if (dw == 0) { + res = FR_INT_ERR; + } else { +/* Start of critical section where an interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of the critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_MINIMIZE == 0 */ +#endif /* FF_FS_MINIMIZE <= 1 */ +#endif /* FF_FS_MINIMIZE <= 2 */ + + + +#if FF_USE_CHMOD && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ + + + +#if FF_USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Logical drive number */ + TCHAR* label, /* Buffer to store the volume label */ + DWORD* vsn /* Variable to store the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; + WCHAR wc; + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read_label(&dj); /* Find a volume label entry */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + WCHAR hs; + + for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + wc = ld_word(dj.dir + XDIR_Label + si * 2); + if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ + hs = wc; continue; + } + wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry */ + while (si < 11) { + wc = dj.dir[si++]; +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ + wc = ff_oem2uni(wc, CODEPAGE); + if (wc != 0) wc = put_utf(wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; +#else /* ANSI/OEM output */ + label[di++] = (TCHAR)wc; +#endif + } + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: + di = BPB_VolIDEx; break; + + case FS_FAT32: + di = BS_VolID32; break; + + default: + di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Volume label to set with heading logical drive number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT di; + WCHAR wc; + static const char badchr[] = "+.,;=[]\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ +#if FF_USE_LFN + DWORD dc; +#endif + + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + mem_set(dirvn, 0, 22); + di = 0; + while (*label) { /* Create volume label in directory form */ + dc = tchar2uni(&label); /* Get a Unicode character */ + if (dc >= 0x10000) { + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */ + dc = 0; + } else { + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; + } + } + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + di * 2, (WCHAR)dc); di++; + } + } else +#endif + { /* On the FAT/FAT32 volume */ + mem_set(dirvn, ' ', 11); + di = 0; + while (*label) { /* Create volume label in directory form */ +#if FF_USE_LFN + dc = tchar2uni(&label); + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0; +#else /* ANSI/OEM input */ + wc = (BYTE)*label++; + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0; + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */ +#if FF_CODE_PAGE == 0 + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#elif FF_CODE_PAGE < 900 + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#endif +#endif + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); + dirvn[di++] = (BYTE)wc; + } + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */ + } + + /* Set volume label */ + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read_label(&dj); /* Get volume label entry */ + if (res == FR_OK) { + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + if (di != 0) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry or an error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (di != 0) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = (BYTE)di; + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LABEL */ + + + +#if FF_USE_EXPAND && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { /* Is it allocated now? */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ + + + +#if FF_USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward Data to the Stream Directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clst2sect(fs, fp->clust); /* Get current data sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; +#if FF_FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (rcnt == 0) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* FF_USE_FORWARD */ + + + +#if FF_USE_MKFS && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create an FAT/exFAT volume */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit (cluster) [byte] */ + void* work, /* Pointer to working buffer (null: use heap memory) */ + UINT len /* Size of working buffer [byte] */ +) +{ + const UINT n_fats = 1; /* Number of FATs for FAT/FAT32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; /* Sector size */ + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i; + int vol; + DSTATUS stat; +#if FF_USE_TRIM || FF_FS_EXFAT + DWORD tbl[3]; +#endif + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume if mounted */ + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ + + /* Check physical drive status */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = FF_MAX_SS; +#endif + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ +#if FF_USE_LFN == 3 + if (!work) { /* Use heap memory for working buffer */ + for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && !(buf = ff_memalloc(szb_buf)); szb_buf /= 2) ; + sz_buf = szb_buf / ss; /* Size of working buffer (sector) */ + } else +#endif + { + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + } + if (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (FF_MULTI_PARTITION && part != 0) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } else { + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (FF_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if FF_FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ +#if FF_USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (au == 0) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* go to next case */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (si == 0 || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb != 0 && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb != 0 && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + + } else +#endif /* FF_FS_EXFAT */ + { /* Create an FAT/FAT32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (pau == 0) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED); + } else { /* FAT volume */ + if (pau == 0) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (au == 0 && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (au == 0 && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if FF_USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + } + + /* Determine system ID in the partition table */ + if (FF_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ + } else { + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (large) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */ + } + } + } + + /* Update partition information */ + if (FF_MULTI_PARTITION && part != 0) { /* Created in the existing partition */ + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ + } else { /* Created as a new single partition */ + if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(((n >> 2) & 0xC0) | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */ + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + + LEAVE_MKFS(FR_OK); +} + + + +#if FF_MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create Partition Table on the Physical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD* szt, /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer (null: use heap memory) */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf; = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + FRESULT res; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + buf = (BYTE*)work; +#if FF_USE_LFN == 3 + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */ +#endif + if (!buf) return FR_NOT_ENOUGH_CORE; + + /* Determine the CHS without any consideration of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, FF_MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */ + if (p_cyl == 0) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; /* End cylinder */ + if (e_cyl >= tot_cyl) LEAVE_MKFS(FR_INVALID_PARAMETER); + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x07; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Number of sectors */ + + /* Next partition */ + b_cyl += p_cyl; + } + st_word(p, 0xAA55); /* MBR signature (always at offset 510) */ + + /* Write it to the MBR */ + res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + LEAVE_MKFS(res); +} + +#endif /* FF_MULTI_PARTITION */ +#endif /* FF_USE_MKFS && !FF_FS_READONLY */ + + + + +#if FF_USE_STRFUNC +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3) +#error Wrong FF_STRF_ENCODE setting +#endif +/*-----------------------------------------------------------------------*/ +/* Get a String from the File */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (items) */ + FIL* fp /* Pointer to the file object */ +) +{ + int nc = 0; + TCHAR *p = buff; + BYTE s[2]; + UINT rc; + WCHAR wc; +#if FF_USE_LFN && ((FF_LFN_UNICODE == 1 && FF_STRF_ENCODE == 3) || (FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3)) + DWORD dc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE == 1 && FF_STRF_ENCODE == 3 + UINT ct; +#endif + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* UTF-16 output */ +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + while (nc < len - 1) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (dbc_1st((BYTE)wc)) { + f_read(fp, s, 1, &rc); + if (rc != 1 || !dbc_2nd(s[0])) continue; + wc = wc << 8 | s[0]; + } + wc = ff_oem2uni(wc, CODEPAGE); + if (wc == 0) continue; +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ + while (nc < len - 1) { + f_read(fp, s, 2, &rc); + if (rc != 2) break; + wc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; +#elif FF_STRF_ENCODE == 3 /* Read a character in UTF-8 */ + while (nc < len - 2) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + dc = s[0]; + if (dc >= 0x80) { + ct = 0; + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } + if (ct == 0) continue; + do { + f_read(fp, s, 1, &rc); + if (rc != 1 || (s[0] & 0xC0) != 0x80) break; + dc = dc << 6 | (s[0] & 0x3F); + } while (--ct); + if (ct || dc < 0x80 || dc >= 0x110000) continue; + } + if (dc >= 0x10000) { + wc = (WCHAR)(0xD800 | ((dc >> 10) - 0x40)); + *p++ = wc; nc++; + wc = (WCHAR)(0xDC00 | (dc & 0x3FF)); + } else { + wc = (WCHAR)dc; + } +#endif + /* Output it in UTF-16 encoding */ + if (FF_USE_STRFUNC == 2 && wc == '\r') continue; + *p++ = wc; nc++; + if (wc == '\n') break; + } + +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3 /* UTF-8 output */ + while (nc < len - 4) { +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (dbc_1st((BYTE)wc)) { + f_read(fp, s, 1, &rc); + if (rc != 1 || !dbc_2nd(s[0])) continue; + wc = wc << 8 | s[0]; + } + dc = ff_oem2uni(wc, CODEPAGE); + if (dc == 0) continue; +#else /* Read a character in UTF-16LE/BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + dc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; + if (IsSurrogate(dc)) { + f_read(fp, s, 2, &rc); + if (rc != 2) break; + wc = (FF_STRF_ENCODE == 1) ? s[1] << 8 | s[0] : s[0] << 8 | s[1]; + if (!IsSurrogateH(dc) || !IsSurrogateL(wc)) continue; + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); + } +#endif + /* Output it in UTF-8 encoding */ + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; + if (dc < 0x80) { /* 1-byte */ + *p++ = (TCHAR)dc; + nc++; + if (dc == '\n') break; + } else { + if (dc < 0x800) { /* 2-byte */ + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 2; + } else { + if (dc < 0x10000) { /* 3-byte */ + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 3; + } else { /* 4-byte */ + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 4; + } + } + } + } + +#else /* Byte-by-byte without any conversion (ANSI/OEM API or UTF-8 to UTF-8) */ + while (nc < len - 1) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (FF_USE_STRFUNC == 2 && wc == '\r') continue; + *p++ = (TCHAR)wc; nc++; + if (wc == '\n') break; + } +#endif + + *p = 0; + return nc ? buff : 0; /* When no data read (EOF or error), return with error. */ +} + + + + +#if !FF_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a Character to the File */ +/*-----------------------------------------------------------------------*/ + +typedef struct { /* Putchar output buffer and work area */ + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */ +#if FF_USE_LFN && FF_LFN_UNICODE == 1 + WCHAR hs; +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 + BYTE bs[4]; + UINT wi, ct; +#endif + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +static +void putc_bfd ( /* Buffered write with code conversion */ + putbuff* pb, + TCHAR c +) +{ + UINT n; + int i, nc; +#if FF_USE_LFN && (FF_LFN_UNICODE == 1 || (FF_LFN_UNICODE == 2 && (FF_STRF_ENCODE != 3))) + WCHAR hs, wc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE == 2 && FF_STRF_ENCODE != 3 + DWORD dc; + TCHAR *tp; +#endif + + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + nc = pb->nchr; /* Write unit count */ + +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* UTF-16 input */ + if (IsSurrogateH(c)) { + pb->hs = c; return; + } + wc = c; hs = pb->hs; pb->hs = 0; + if (hs != 0) { + if (!IsSurrogateL(wc)) hs = 0; + } else { + if (IsSurrogateL(wc)) return; + } +#if FF_STRF_ENCODE == 3 /* Write it in UTF-8 */ + if (hs != 0) { /* 4-byte */ + nc += 4; + hs = (hs & 0x3FF) + 0x40; + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } else { + if (wc < 0x80) { /* 1-byte */ + nc++; + pb->buf[i++] = (BYTE)wc; + } else { + if (wc < 0x800) { /* 2-byte */ + nc += 2; + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); + } else { /* 3-byte */ + nc += 3; + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } + } +#endif +#else /* UTF-8 input */ + for (;;) { + if (pb->ct == 0) { /* Out of multi-byte sequence? */ + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ + if ((BYTE)c < 0x80) break; /* 1-byte? */ + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */ + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */ + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */ + return; + } else { /* In the multi-byte sequence */ + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ + pb->ct = 0; continue; + } + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ + if (--pb->ct == 0) break; /* End of sequence? */ + return; + } + } +#if FF_STRF_ENCODE == 3 /* Write it in UTF-8 */ + pb->buf[i++] = pb->bs[0]; nc++; + if (pb->bs[0] >= 0xC0) { + pb->buf[i++] = pb->bs[1]; nc++; + } + if (pb->bs[0] >= 0xE0) { + pb->buf[i++] = pb->bs[2]; nc++; + } + if (pb->bs[0] >= 0xF0) { + pb->buf[i++] = pb->bs[3]; nc++; + } +#else /* Write it in UTF-16 or ANSI/OEM */ + tp = (TCHAR*)pb->bs; + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ + if (dc == 0xFFFFFFFF) return; + wc = (WCHAR)dc; + hs = (WCHAR)(dc >> 16); +#endif +#endif +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 && FF_STRF_ENCODE != 3 +#if FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + if (hs != 0) { + pb->buf[i++] = (BYTE)(hs >> 8); + pb->buf[i++] = (BYTE)hs; + nc++; + } + pb->buf[i++] = (BYTE)(wc >> 8); + pb->buf[i++] = (BYTE)wc; + nc++; +#elif FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + if (hs != 0) { + pb->buf[i++] = (BYTE)hs; + pb->buf[i++] = (BYTE)(hs >> 8); + nc++; + } + pb->buf[i++] = (BYTE)wc; + pb->buf[i++] = (BYTE)(wc >> 8); + nc++; +#else /* Write a character in ANSI/OEM */ + if (hs != 0) return; + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ + if (wc == 0) return;; + if (wc >= 0x100) { + pb->buf[i++] = (BYTE)(wc >> 8); nc++; + } + pb->buf[i++] = (BYTE)wc; nc++; +#endif +#endif + +#else /* ANSI/OEM input */ + pb->buf[i++] = (BYTE)c; + nc++; +#endif + + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &n); + i = (n == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr = nc; +} + + +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + mem_set(pb, 0, sizeof (putbuff)); + pb->fp = fp; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a Formatted String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + if (c == '*') { /* Minimum width by argument */ + w = va_arg(arp, int); + c = *fmt++; + } else { + while (IsDigit(c)) { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } + } + if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (c == 0) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Atgument type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { /* Right padded */ + while (j++ < w) putc_bfd(&pb, ' ') ; + } + while (*p) putc_bfd(&pb, *p++) ; /* String body */ + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ + continue; + + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + + case 'B' : /* Unsigned binary */ + r = 2; break; + + case 'O' : /* Unsigned octal */ + r = 8; break; + + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + + case 'X' : /* Unsigned hexdecimal */ + r = 16; break; + + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof *str); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ + } + do { + putc_bfd(&pb, str[--i]); /* Number body */ + } while (i); + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_STRFUNC */ + + + +#if FF_CODE_PAGE == 0 +/*-----------------------------------------------------------------------*/ +/* Set Active Codepage for the Path Name */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setcp ( + WORD cp /* Value to be set as active code page */ +) +{ + static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const BYTE *const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; + UINT i; + + + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */ + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */ + + CodePage = cp; + if (cp >= 900) { /* DBCS */ + ExCvt = 0; + DbcTbl = tables[i]; + } else { /* SBCS */ + ExCvt = tables[i]; + DbcTbl = 0; + } + return FR_OK; +} +#endif /* FF_CODE_PAGE == 0 */ + diff --git a/src/hwinit/ff.h b/src/hwinit/ff.h new file mode 100644 index 0000000..da57ca8 --- /dev/null +++ b/src/hwinit/ff.h @@ -0,0 +1,366 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem module R0.13a / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/----------------------------------------------------------------------------*/ + + +#ifndef FF_DEFINED +#define FF_DEFINED 89352 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if FF_DEFINED != FFCONF_DEF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#endif + + + +/* Type of path name strings on FatFs API */ + +#ifndef _INC_TCHAR +#define _INC_TCHAR + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ +typedef char TCHAR; +#define _T(x) u8 ## x +#define _TEXT(x) u8 ## x +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 2) +#error Wrong FF_LFN_UNICODE setting +#else /* ANSI/OEM code in SBCS/DBCS */ +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* Type of file size variables */ + +#if FF_FS_EXFAT +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* Filesystem object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* Filesystem type (0:N/A) */ + BYTE pdrv; /* Physical drive number */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* Volume mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if FF_MAX_SS != FF_MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if FF_USE_LFN + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if FF_FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ +#endif +#if FF_FS_REENTRANT + FF_SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !FF_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if FF_FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#if FF_FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (FFOBJID) */ + +typedef struct { + FATFS* fs; /* Pointer to the hosting volume of this object */ + WORD id; /* Hosting volume mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:flagmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if FF_FS_EXFAT + DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ +#endif +#if FF_FS_LOCK + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} FFOBJID; + + + +/* File object structure (FIL) */ + +typedef struct { + FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !FF_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ +#endif +#if FF_USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !FF_FS_TINY + BYTE buf[FF_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FFOBJID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if FF_USE_LFN + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if FF_USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if FF_USE_LFN + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ +#else + TCHAR fname[12 + 1]; /* File name */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (DIR* dp); /* Close an open directory */ +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +FRESULT f_setcp (WORD cp); /* Set current code page */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) +#define f_unmount(path) f_mount(0, path, 0) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !FF_FS_READONLY && !FF_FS_NORTC +DWORD get_fattime (void); +#endif + +/* LFN support functions */ +#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ +#endif +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif + +/* Sync functions */ +#if FF_FS_REENTRANT +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* FF_DEFINED */ diff --git a/src/hwinit/ffconf.h b/src/hwinit/ffconf.h new file mode 100644 index 0000000..811b454 --- /dev/null +++ b/src/hwinit/ffconf.h @@ -0,0 +1,283 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - Configuration file +/---------------------------------------------------------------------------*/ + +#define FFCONF_DEF 89352 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define FF_FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: Basic functions are fully enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define FF_USE_STRFUNC 2 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define FF_USE_FIND 1 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + + +#define FF_USE_MKFS 0 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define FF_USE_FASTSEEK 0 +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define FF_USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define FF_USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ + + +#define FF_USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define FF_USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define FF_CODE_PAGE 850 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect code page setting can cause a file open failure. +/ +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +/ 0 - Include all code pages above and configured by f_setcp() +*/ + + +#define FF_USE_LFN 1 +#define FF_MAX_LFN 255 +/* The FF_USE_LFN switches the support for LFN (long file name). +/ +/ 0: Disable LFN. FF_MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can +/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN +/ specification. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree() in ffsystem.c, need to be added to the project. */ + + +#define FF_LFN_UNICODE 0 +/* This option switches the character encoding on the API when LFN is enabled. +/ +/ 0: ANSI/OEM in current CP (TCHAR = char) +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) +/ 2: Unicode in UTF-8 (TCHAR = char) +/ +/ Also behavior of string I/O functions will be affected by this option. +/ When LFN is not enabled, this option has no effect. */ + + +#define FF_LFN_BUF 255 +#define FF_SFN_BUF 12 +/* This set of options defines size of file name members in the FILINFO structure +/ which is used to read out directory items. These values should be suffcient for +/ the file names to read. The maximum possible length of the read file name depends +/ on character encoding. When LFN is not enabled, these options have no effect. */ + + +#define FF_STRF_ENCODE 0 +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), +/ f_putc(), f_puts and f_printf() convert the character encoding in it. +/ This option selects assumption of character encoding ON THE FILE to be +/ read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + +#define FF_FS_RPATH 0 +/* This option configures support for relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define FF_VOLUMES 1 +/* Number of volumes (logical drives) to be used. (1-10) */ + + +#define FF_STR_VOLUME_ID 0 +#define FF_VOLUME_STRS "sd" +/* FF_STR_VOLUME_ID switches string support for volume ID. +/ When FF_STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. FF_VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to FF_VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ + + +#define FF_MULTI_PARTITION 0 +/* This option switches support for multiple volumes on the physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When this function is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ + + +#define FF_MIN_SS 512 +#define FF_MAX_SS 512 +/* This set of options configures the range of sector size to be supported. (512, +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured +/ for variable sector size mode and disk_ioctl() function needs to implement +/ GET_SECTOR_SIZE command. */ + + +#define FF_USE_TRIM 0 +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + +#define FF_FS_NOFSINFO 1 +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the filesystem object (FATFS) is used for the file data transfer. */ + + +#define FF_FS_EXFAT 1 +/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ + + +#define FF_FS_NORTC 1 +#define FF_NORTC_MON 1 +#define FF_NORTC_MDAY 1 +#define FF_NORTC_YEAR 2018 +/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to read current time form real-time clock. FF_NORTC_MON, +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */ + + +#define FF_FS_LOCK 0 +/* The option FF_FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + + +#define FF_FS_REENTRANT 0 +#define FF_FS_TIMEOUT 1000 +#define FF_SYNC_t HANDLE +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + +/* #include // O/S definitions */ + + + +/*--- End of configuration options ---*/ diff --git a/src/hwinit/ffunicode.c b/src/hwinit/ffunicode.c new file mode 100644 index 0000000..e1f68a9 --- /dev/null +++ b/src/hwinit/ffunicode.c @@ -0,0 +1,608 @@ +/*------------------------------------------------------------------------*/ +/* Unicode handling functions for FatFs R0.13a */ +/*------------------------------------------------------------------------*/ +/* This module will occupy a huge memory in the .const section when the / +/ FatFs is configured for LFN with DBCS. If the system has any Unicode / +/ utilitiy for the code conversion, this module should be modified to use / +/ that function to avoid silly memory consumption. / +/-------------------------------------------------------------------------*/ +/* +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +*/ + + +#include "ff.h" + +#if FF_USE_LFN /* This module is blanked when non-LFN configuration */ + +#if FF_DEFINED != 89352 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + +#define MERGE2(a, b) a ## b +#define CVTBL(tbl, cp) MERGE2(tbl, cp) + + +/*------------------------------------------------------------------------*/ +/* Code Conversion Tables */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0 +static +const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0 +static +const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0 +static +const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0 +static +const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0 +static +const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0 +static +const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0 +static +const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0 +static +const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0 +static +const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0 +static +const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, + 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0 +static +const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0 +static +const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0 +static +const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, + 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, + 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0 +static +const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */ + 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, + 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, + 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, + 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, + 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 +}; +#endif +#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0 +static +const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0 +static +const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0 +static +const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */ + 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, + 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, + 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, + 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, + 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 +}; +#endif + + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for static code page configuration */ +/* SBCS fixed code page */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900 +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + WCHAR c = 0; + const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it a valid code? */ + for (c = 0; c < 0x80 && uni != p[c]; c++) ; + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + WCHAR c = 0; + const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */ + if (oem < 0x100) c = p[oem - 0x80]; + } + } + + return c; +} + +#endif + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for static code page configuration */ +/* DBCS fixed code page */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE >= 900 +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0, uc; + UINT i, n, li, hi; + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000) { /* Is it in BMP? */ + if (cp == FF_CODE_PAGE) { /* Is it a valid code? */ + uc = (WCHAR)uni; + p = CVTBL(uni2oem, FF_CODE_PAGE); + hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1; + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + } + + return c; +} + + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0; + UINT i, n, li, hi; + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */ + p = CVTBL(oem2uni, FF_CODE_PAGE); + hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1; + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (oem == p[i * 2]) break; + if (oem > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + + return c; +} +#endif + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for dynamic code page configuration */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE == 0 + +static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0}; +static const WCHAR *const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0}; + + +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0, uc; + UINT i, n, li, hi; + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WCHAR)uni; + p = 0; + if (cp < 900) { /* SBCS */ + for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */ + p = cp_table[i]; + if (p) { /* Is it a valid CP ? */ + for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */ + c = (c + 0x80) & 0xFF; + } + } else { /* DBCS */ + switch (cp) { + case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break; + case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break; + case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break; + case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break; + } + if (p) { /* Is it a valid code page? */ + li = 0; + for (n = 16; n; n--) { /* Find OEM code */ + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + } + } + + return c; +} + + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0; + UINT i, n, li, hi; + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + p = 0; + if (cp < 900) { /* SBCS */ + for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */ + p = cp_table[i]; + if (p) { /* Is it a valid CP ? */ + if (oem < 0x100) c = p[oem - 0x80]; + } + } else { /* DBCS */ + switch (cp) { + case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break; + case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break; + case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break; + case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break; + } + if (p) { + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (oem == p[i * 2]) break; + if (oem > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + } + + return c; +} +#endif + + + +/*------------------------------------------------------------------------*/ +/* Unicode up-case conversion */ +/*------------------------------------------------------------------------*/ + +DWORD ff_wtoupper ( /* Returns up-converted code point */ + DWORD uni /* Unicode code point to be up-converted */ +) +{ + /* Compressed upper conversion table */ + static const WORD cvt1[] = { /* U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128, + 0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 + }; + static const WORD cvt2[] = { /* U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF3,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 + }; + const WORD *p; + WORD uc, bc, nc, cmd; + + + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WORD)uni; + p = uc < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get block base */ + if (!bc || uc < bc) break; + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (uc < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: uc = p[uc - bc]; break; /* Table conversion */ + case 1: uc -= (uc - bc) & 1; break; /* Case pairs */ + case 2: uc -= 16; break; /* Shift -16 */ + case 3: uc -= 32; break; /* Shift -32 */ + case 4: uc -= 48; break; /* Shift -48 */ + case 5: uc -= 26; break; /* Shift -26 */ + case 6: uc += 8; break; /* Shift +8 */ + case 7: uc -= 80; break; /* Shift -80 */ + case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (!cmd) p += nc; + } + uni = uc; + } + + return uni; +} + + +#endif /* #if FF_USE_LFN */ diff --git a/src/hwinit/gfx.c b/src/hwinit/gfx.c new file mode 100644 index 0000000..d67d1e8 --- /dev/null +++ b/src/hwinit/gfx.c @@ -0,0 +1,522 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include +#include "gfx.h" + +static const u8 _gfx_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( ) + 0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, // Char 033 (!) + 0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, // Char 034 (") + 0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, // Char 035 (#) + 0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, // Char 036 ($) + 0x00, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, // Char 037 (%) + 0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, // Char 038 (&) + 0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // Char 039 (') + 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, // Char 040 (() + 0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, // Char 041 ()) + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*) + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // Char 043 (+) + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 044 (,) + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // Char 045 (-) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // Char 046 (.) + 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // Char 047 (/) + 0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, // Char 048 (0) + 0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, // Char 049 (1) + 0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, // Char 050 (2) + 0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, // Char 051 (3) + 0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, // Char 052 (4) + 0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, // Char 053 (5) + 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, // Char 054 (6) + 0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, // Char 055 (7) + 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // Char 056 (8) + 0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, // Char 057 (9) + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, // Char 058 (:) + 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 059 (;) + 0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, // Char 060 (<) + 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // Char 061 (=) + 0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, // Char 062 (>) + 0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // Char 063 (?) + 0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, // Char 064 (@) + 0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 065 (A) + 0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 066 (B) + 0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, // Char 067 (C) + 0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, // Char 068 (D) + 0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, // Char 069 (E) + 0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, // Char 070 (F) + 0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, // Char 071 (G) + 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 072 (H) + 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 073 (I) + 0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, // Char 074 (J) + 0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, // Char 075 (K) + 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, // Char 076 (L) + 0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, // Char 077 (M) + 0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, // Char 078 (N) + 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 079 (O) + 0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, // Char 080 (P) + 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, // Char 081 (Q) + 0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, // Char 082 (R) + 0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, // Char 083 (S) + 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 084 (T) + 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 085 (U) + 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 086 (V) + 0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, // Char 087 (W) + 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // Char 088 (X) + 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Char 089 (Y) + 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, // Char 090 (Z) + 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, // Char 091 ([) + 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // Char 092 (\) + 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, // Char 093 (]) + 0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, // Char 094 (^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_) + 0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // Char 096 (`) + 0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, // Char 097 (a) + 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 098 (b) + 0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, // Char 099 (c) + 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // Char 100 (d) + 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, // Char 101 (e) + 0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, // Char 102 (f) + 0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, // Char 103 (g) + 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, // Char 104 (h) + 0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, // Char 105 (i) + 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, // Char 106 (j) + 0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, // Char 107 (k) + 0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 108 (l) + 0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m) + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 110 (n) + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 111 (o) + 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, // Char 112 (p) + 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, // Char 113 (q) + 0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, // Char 114 (r) + 0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, // Char 115 (s) + 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, // Char 116 (t) + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // Char 117 (u) + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 118 (v) + 0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, // Char 119 (w) + 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // Char 120 (x) + 0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, // Char 121 (y) + 0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, // Char 122 (z) + 0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 0x18, // Char 123 ({) + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Char 124 (|) + 0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 0x0C, // Char 125 (}) + 0x00, 0x00, 0x00, 0x4C, 0x32, 0x00, 0x00, 0x00 // Char 126 (~) +}; + +void gfx_init_ctxt(gfx_ctxt_t *ctxt, u32 *fb, u32 width, u32 height, u32 stride) +{ + ctxt->fb = fb; + ctxt->width = width; + ctxt->height = height; + ctxt->stride = stride; +} + +void gfx_clear_grey(gfx_ctxt_t *ctxt, u8 color) +{ + memset(ctxt->fb, color, 0x3C0000); +} + +void gfx_clear_color(gfx_ctxt_t *ctxt, u32 color) +{ + for (u32 i = 0; i < ctxt->height * ctxt->stride; i++) + ctxt->fb[i] = color; +} + +void gfx_con_init(gfx_con_t *con, gfx_ctxt_t *ctxt) +{ + con->gfx_ctxt = ctxt; + con->fntsz = 8; + con->x = 0; + con->y = 0; + con->savedx = 0; + con->savedy = 0; + con->fgcol = 0xFFCCCCCC; + con->fillbg = 0; + con->bgcol = 0xFF1B1B1B; + con->mute = 0; +} + +void gfx_con_setcol(gfx_con_t *con, u32 fgcol, int fillbg, u32 bgcol) +{ + con->fgcol = fgcol; + con->fillbg = fillbg; + con->bgcol = bgcol; +} + +void gfx_con_getpos(gfx_con_t *con, u32 *x, u32 *y) +{ + *x = con->x; + *y = con->y; +} + +void gfx_con_setpos(gfx_con_t *con, u32 x, u32 y) +{ + con->x = x; + con->y = y; +} + +void gfx_putc(gfx_con_t *con, char c) +{ + // Duplicate code for performance reasons. + switch (con->fntsz) + { + case 16: + if (c >= 32 && c <= 126) + { + u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)]; + u32 *fb = con->gfx_ctxt->fb + con->x + con->y * con->gfx_ctxt->stride; + + for (u32 i = 0; i < 16; i+=2) + { + u8 v = *cbuf++; + for (u32 k = 0; k < 2; k++) + { + for (u32 j = 0; j < 8; j++) + { + if (v & 1) + { + *fb = con->fgcol; + fb++; + *fb = con->fgcol; + } + else if (con->fillbg) + { + *fb = con->bgcol; + fb++; + *fb = con->bgcol; + } + else + fb++; + v >>= 1; + fb++; + } + fb += con->gfx_ctxt->stride - 16; + v = *cbuf; + } + } + con->x += 16; + } + else if (c == '\n') + { + con->x = 0; + con->y +=16; + if (con->y > con->gfx_ctxt->height - 16) + con->y = 0; + } + break; + case 8: + default: + if (c >= 32 && c <= 126) + { + u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)]; + u32 *fb = con->gfx_ctxt->fb + con->x + con->y * con->gfx_ctxt->stride; + for (u32 i = 0; i < 8; i++) + { + u8 v = *cbuf++; + for (u32 j = 0; j < 8; j++) + { + if (v & 1) + *fb = con->fgcol; + else if (con->fillbg) + *fb = con->bgcol; + v >>= 1; + fb++; + } + fb += con->gfx_ctxt->stride - 8; + } + con->x += 8; + } + else if (c == '\n') + { + con->x = 0; + con->y += 8; + if (con->y > con->gfx_ctxt->height - 8) + con->y = 0; + } + break; + } + +} + +void gfx_puts(gfx_con_t *con, const char *s) +{ + if (!s || con->mute) + return; + + for (; *s; s++) + gfx_putc(con, *s); +} + +static void _gfx_putn(gfx_con_t *con, u32 v, int base, char fill, int fcnt) +{ + char buf[65]; + static const char digits[] = "0123456789ABCDEFghijklmnopqrstuvwxyz"; + char *p; + int c = fcnt; + + if (base > 36) + return; + + p = buf + 64; + *p = 0; + do + { + c--; + *--p = digits[v % base]; + v /= base; + } while (v); + + if (fill != 0) + { + while (c > 0) + { + *--p = fill; + c--; + } + } + + gfx_puts(con, p); +} + +void gfx_put_small_sep(gfx_con_t *con) +{ + u8 prevFontSize = con->fntsz; + con->fntsz = 8; + gfx_putc(con, '\n'); + con->fntsz = prevFontSize; +} + +void gfx_put_big_sep(gfx_con_t *con) +{ + u8 prevFontSize = con->fntsz; + con->fntsz = 16; + gfx_putc(con, '\n'); + con->fntsz = prevFontSize; +} + +void gfx_printf(gfx_con_t *con, const char *fmt, ...) +{ + if (con->mute) + return; + + va_list ap; + int fill, fcnt; + + va_start(ap, fmt); + while(*fmt) + { + if(*fmt == '%') + { + fmt++; + fill = 0; + fcnt = 0; + if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ') + { + fcnt = *fmt; + fmt++; + if (*fmt >= '0' && *fmt <= '9') + { + fill = fcnt; + fcnt = *fmt - '0'; + fmt++; + } + else + { + fill = ' '; + fcnt -= '0'; + } + } + switch(*fmt) + { + case 'c': + gfx_putc(con, va_arg(ap, u32)); + break; + case 's': + gfx_puts(con, va_arg(ap, char *)); + break; + case 'd': + _gfx_putn(con, va_arg(ap, u32), 10, fill, fcnt); + break; + case 'x': + case 'X': + _gfx_putn(con, va_arg(ap, u32), 16, fill, fcnt); + break; + case 'k': + con->fgcol = va_arg(ap, u32); + break; + case 'K': + con->bgcol = va_arg(ap, u32); + con->fillbg = fcnt; + break; + case '%': + gfx_putc(con, '%'); + break; + case '\0': + goto out; + default: + gfx_putc(con, '%'); + gfx_putc(con, *fmt); + break; + } + } + else + gfx_putc(con, *fmt); + fmt++; + } + + out: + va_end(ap); +} + +void gfx_hexdump(gfx_con_t *con, u32 base, const u8 *buf, u32 len) +{ + if (con->mute) + return; + + u8 prevFontSize = con->fntsz; + con->fntsz = 8; + for(u32 i = 0; i < len; i++) + { + if(i % 0x10 == 0) + { + if(i != 0) + { + gfx_puts(con, "| "); + for(u32 j = 0; j < 0x10; j++) + { + u8 c = buf[i - 0x10 + j]; + if(c >= 32 && c <= 126) + gfx_putc(con, c); + else + gfx_putc(con, '.'); + } + gfx_putc(con, '\n'); + } + gfx_printf(con, "%08x: ", base + i); + } + gfx_printf(con, "%02x ", buf[i]); + if (i == len - 1) + { + int ln = len % 0x10 != 0; + u32 k = 0x10 - 1; + if (ln) + { + k = (len & 0xF) - 1; + for (u32 j = 0; j < 0x10 - k; j++) + gfx_puts(con, " "); + } + gfx_puts(con, "| "); + for(u32 j = 0; j < (ln ? k : k + 1); j++) + { + u8 c = buf[i - k + j]; + if(c >= 32 && c <= 126) + gfx_putc(con, c); + else + gfx_putc(con, '.'); + } + gfx_putc(con, '\n'); + } + } + gfx_putc(con, '\n'); + con->fntsz = prevFontSize; +} + +static int abs(int x) +{ + if (x < 0) + return -x; + return x; +} + +void gfx_set_pixel(gfx_ctxt_t *ctxt, u32 x, u32 y, u32 color) +{ + ctxt->fb[x + y * ctxt->stride] = color; +} + +void gfx_line(gfx_ctxt_t *ctxt, int x0, int y0, int x1, int y1, u32 color) +{ + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = (dx > dy ? dx : -dy) / 2, e2; + + while (1) + { + gfx_set_pixel(ctxt, x0, y0, color); + if (x0 == x1 && y0 == y1) + break; + e2 = err; + if (e2 >-dx) + { + err -= dy; + x0 += sx; + } + if (e2 < dy) + { + err += dx; + y0 += sy; + } + } +} + +void gfx_set_rect_grey(gfx_ctxt_t *ctxt, const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y) +{ + u32 pos = 0; + for (u32 y = pos_y; y < (pos_y + size_y); y++) + { + for (u32 x = pos_x; x < (pos_x + size_x); x++) + { + memset(&ctxt->fb[x + y*ctxt->stride], buf[pos], 4); + pos++; + } + } +} + + +void gfx_set_rect_rgb(gfx_ctxt_t *ctxt, const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y) +{ + u32 pos = 0; + for (u32 y = pos_y; y < (pos_y + size_y); y++) + { + for (u32 x = pos_x; x < (pos_x + size_x); x++) + { + ctxt->fb[x + y*ctxt->stride] = buf[pos + 2] | (buf[pos + 1] << 8) | (buf[pos] << 16); + pos+=3; + } + } +} + +void gfx_set_rect_argb(gfx_ctxt_t *ctxt, const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y) +{ + u32 pos = 0; + for (u32 y = pos_y; y < (pos_y + size_y); y++) + { + for (u32 x = pos_x; x < (pos_x + size_x); x++) + { + ctxt->fb[x + y*ctxt->stride] = buf[pos]; + pos+=1; + } + } +} + +void gfx_render_bmp_argb(gfx_ctxt_t *ctxt, const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y) +{ + for (u32 y = pos_y; y < (pos_y + size_y); y++) + { + for (u32 x = pos_x; x < (pos_x + size_x); x++) + ctxt->fb[x + y*ctxt->stride] = buf[(size_y + pos_y - 1 - y ) * size_x + x - pos_x]; + } +} \ No newline at end of file diff --git a/src/hwinit/gfx.h b/src/hwinit/gfx.h new file mode 100644 index 0000000..987dcac --- /dev/null +++ b/src/hwinit/gfx.h @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _GFX_H_ +#define _GFX_H_ + +#include "types.h" + +#define RED 0xFF0000FF +#define GREEN 0xFF00FF00 +#define BLUE 0xFFFF0000 +#define YELLOW 0xFF00FFFF +#define ORANGE 0xFF3891FF +#define WHITE 0xFFFFFFFF + +typedef struct _gfx_ctxt_t +{ + u32 *fb; + u32 width; + u32 height; + u32 stride; +} gfx_ctxt_t; + +typedef struct _gfx_con_t +{ + gfx_ctxt_t *gfx_ctxt; + u32 fntsz; + u32 x; + u32 y; + u32 savedx; + u32 savedy; + u32 fgcol; + int fillbg; + u32 bgcol; + int mute; +} gfx_con_t; + +gfx_ctxt_t gfx_ctxt; +gfx_con_t gfx_con; +#define print(...) gfx_printf(&gfx_con, __VA_ARGS__) +#define printHex(...) gfx_hexdump(&gfx_con, __VA_ARGS__) + +void gfx_init_ctxt(gfx_ctxt_t *ctxt, u32 *fb, u32 width, u32 height, u32 stride); +void gfx_clear_grey(gfx_ctxt_t *ctxt, u8 color); +void gfx_clear_color(gfx_ctxt_t *ctxt, u32 color); +void gfx_con_init(gfx_con_t *con, gfx_ctxt_t *ctxt); +void gfx_con_setcol(gfx_con_t *con, u32 fgcol, int fillbg, u32 bgcol); +void gfx_con_getpos(gfx_con_t *con, u32 *x, u32 *y); +void gfx_con_setpos(gfx_con_t *con, u32 x, u32 y); +void gfx_putc(gfx_con_t *con, char c); +void gfx_puts(gfx_con_t *con, const char *s); +void gfx_printf(gfx_con_t *con, const char *fmt, ...); +void gfx_hexdump(gfx_con_t *con, u32 base, const u8 *buf, u32 len); + +void gfx_set_pixel(gfx_ctxt_t *ctxt, u32 x, u32 y, u32 color); +void gfx_line(gfx_ctxt_t *ctxt, int x0, int y0, int x1, int y1, u32 color); +void gfx_put_small_sep(gfx_con_t *con); +void gfx_put_big_sep(gfx_con_t *con); +void gfx_set_rect_grey(gfx_ctxt_t *ctxt, const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y); +void gfx_set_rect_rgb(gfx_ctxt_t *ctxt, const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y); +void gfx_set_rect_argb(gfx_ctxt_t *ctxt, const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y); +void gfx_render_bmp_argb(gfx_ctxt_t *ctxt, const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y); + +#endif \ No newline at end of file diff --git a/src/hwinit/gpio.c b/src/hwinit/gpio.c new file mode 100644 index 0000000..fb75ee8 --- /dev/null +++ b/src/hwinit/gpio.c @@ -0,0 +1,94 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "gpio.h" +#include "t210.h" + +static const u16 _gpio_cnf[31] = { + 0x000, 0x004, 0x008, 0x00C, + 0x100, 0x104, 0x108, 0x10C, + 0x200, 0x204, 0x208, 0x20C, + 0x300, 0x304, 0x308, 0x30C, + 0x400, 0x404, 0x408, 0x40C, + 0x500, 0x504, 0x508, 0x50C, + 0x600, 0x604, 0x608, 0x60C, + 0x700, 0x704, 0x708 +}; + +static const u16 _gpio_oe[31] = { + 0x010, 0x014, 0x018, 0x01C, + 0x110, 0x114, 0x118, 0x11C, + 0x210, 0x214, 0x218, 0x21C, + 0x310, 0x314, 0x318, 0x31C, + 0x410, 0x414, 0x418, 0x41C, + 0x510, 0x514, 0x518, 0x51C, + 0x610, 0x614, 0x618, 0x61C, + 0x710, 0x714, 0x718 +}; + +static const u16 _gpio_out[31] = { + 0x020, 0x024, 0x028, 0x02C, + 0x120, 0x124, 0x128, 0x12C, + 0x220, 0x224, 0x228, 0x22C, + 0x320, 0x324, 0x328, 0x32C, + 0x420, 0x424, 0x428, 0x42C, + 0x520, 0x524, 0x528, 0x52C, + 0x620, 0x624, 0x628, 0x62C, + 0x720, 0x724, 0x728 +}; + +static const u16 _gpio_in[31] = { + 0x030, 0x034, 0x038, 0x03C, + 0x130, 0x134, 0x138, 0x13C, + 0x230, 0x234, 0x238, 0x23C, + 0x330, 0x334, 0x338, 0x33C, + 0x430, 0x434, 0x438, 0x43C, + 0x530, 0x534, 0x538, 0x53C, + 0x630, 0x634, 0x638, 0x63C, + 0x730, 0x734, 0x738 +}; + +void gpio_config(u32 port, u32 pins, int mode) +{ + if (mode) + GPIO(_gpio_cnf[port]) |= pins; + else + GPIO(_gpio_cnf[port]) &= ~pins; + (void)GPIO(_gpio_cnf[port]); +} + +void gpio_output_enable(u32 port, u32 pins, int enable) +{ + if (enable) + GPIO(_gpio_oe[port]) |= pins; + else + GPIO(_gpio_oe[port]) &= ~pins; + (void)GPIO(_gpio_oe[port]); +} + +void gpio_write(u32 port, u32 pins, int high) +{ + if (high) + GPIO(_gpio_out[port]) |= pins; + else + GPIO(_gpio_out[port]) &= ~pins; + (void)GPIO(_gpio_out[port]); +} + +int gpio_read(u32 port, u32 pins) +{ + return (GPIO(_gpio_in[port]) & pins) ? 1 : 0; +} diff --git a/src/hwinit/gpio.h b/src/hwinit/gpio.h new file mode 100644 index 0000000..39e8c80 --- /dev/null +++ b/src/hwinit/gpio.h @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include "types.h" + +#define GPIO_MODE_SPIO 0 +#define GPIO_MODE_GPIO 1 +#define GPIO_OUTPUT_DISABLE 0 +#define GPIO_OUTPUT_ENABLE 1 +#define GPIO_LOW 0 +#define GPIO_HIGH 1 + +/*! GPIO pins (0-7 for each port). */ +#define GPIO_PIN_0 (1 << 0) +#define GPIO_PIN_1 (1 << 1) +#define GPIO_PIN_2 (1 << 2) +#define GPIO_PIN_3 (1 << 3) +#define GPIO_PIN_4 (1 << 4) +#define GPIO_PIN_5 (1 << 5) +#define GPIO_PIN_6 (1 << 6) +#define GPIO_PIN_7 (1 << 7) + +/*! GPIO ports (A-EE). */ +#define GPIO_PORT_A 0 +#define GPIO_PORT_B 1 +#define GPIO_PORT_C 2 +#define GPIO_PORT_D 3 +#define GPIO_PORT_E 4 +#define GPIO_PORT_F 5 +#define GPIO_PORT_G 6 +#define GPIO_PORT_H 7 +#define GPIO_PORT_I 8 +#define GPIO_PORT_J 9 +#define GPIO_PORT_K 10 +#define GPIO_PORT_L 11 +#define GPIO_PORT_M 12 +#define GPIO_PORT_N 13 +#define GPIO_PORT_O 14 +#define GPIO_PORT_P 15 +#define GPIO_PORT_Q 16 +#define GPIO_PORT_R 17 +#define GPIO_PORT_S 18 +#define GPIO_PORT_T 19 +#define GPIO_PORT_U 20 +#define GPIO_PORT_V 21 +#define GPIO_PORT_W 22 +#define GPIO_PORT_X 23 +#define GPIO_PORT_Y 24 +#define GPIO_PORT_Z 25 +#define GPIO_PORT_AA 26 +#define GPIO_PORT_BB 27 +#define GPIO_PORT_CC 28 +#define GPIO_PORT_DD 29 +#define GPIO_PORT_EE 30 + +void gpio_config(u32 port, u32 pins, int mode); +void gpio_output_enable(u32 port, u32 pins, int enable); +void gpio_write(u32 port, u32 pins, int high); +int gpio_read(u32 port, u32 pins); + +#endif diff --git a/src/hwinit/heap.c b/src/hwinit/heap.c new file mode 100644 index 0000000..e61a186 --- /dev/null +++ b/src/hwinit/heap.c @@ -0,0 +1,142 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "heap.h" + +typedef struct _hnode +{ + int used; + u32 size; + struct _hnode *prev; + struct _hnode *next; +} hnode_t; + +typedef struct _heap +{ + u32 start; + hnode_t *first; +} heap_t; + +static void _heap_create(heap_t *heap, u32 start) +{ + heap->start = start; + heap->first = NULL; +} + +static u32 _heap_alloc(heap_t *heap, u32 size) +{ + hnode_t *node, *new; + int search = 1; + + size = ALIGN(size, 0x10); + + if (!heap->first) + { + node = (hnode_t *)heap->start; + node->used = 1; + node->size = size; + node->prev = NULL; + node->next = NULL; + heap->first = node; + + return (u32)node + sizeof(hnode_t); + } + + node = heap->first; + while (search) + { + if (!node->used && size + sizeof(hnode_t) < node->size) + { + new = (hnode_t *)((u32)node + sizeof(hnode_t) + size); + + new->size = node->size - sizeof(hnode_t) - size; + node->size = size; + node->used = 1; + new->used = 0; + new->next = node->next; + new->prev = node; + node->next = new; + + return (u32)node + sizeof(hnode_t); + } + if (node->next) + node = node->next; + else + search = 0; + } + + new = (hnode_t *)((u32)node + sizeof(hnode_t) + node->size); + new->used = 1; + new->size = size; + new->prev = node; + new->next = NULL; + node->next = new; + + return (u32)new + sizeof(hnode_t); +} + +static void _heap_free(heap_t *heap, u32 addr) +{ + hnode_t *node = (hnode_t *)(addr - sizeof(hnode_t)); + node->used = 0; + node = heap->first; + while (node) + { + if (!node->used) + { + if (node->prev && !node->prev->used) + { + node->prev->size += node->size + sizeof(hnode_t); + node->prev->next = node->next; + if (node->next) + node->next->prev = node->prev; + } + } + node = node->next; + } +} + +static heap_t _heap; + +void heap_init(u32 base) { + _heap_create(&_heap, base); +} + +void *malloc(u32 size) { + return (void *)_heap_alloc(&_heap, size); +} + +void *realloc (void * ptr, size_t size) { + hnode_t *node = (hnode_t *) ptr - 1; + void *new = malloc(size); + if (new && ptr) { + memcpy(new, ptr, node -> size); + free(ptr); + } + return new; +} + +void *calloc(u32 num, u32 size) { + void *res = (void *)_heap_alloc(&_heap, num * size); + memset(res, 0, num * size); + return res; +} + +void free(void *buf) { + if (buf != NULL) + _heap_free(&_heap, (u32)buf); +} diff --git a/src/hwinit/heap.h b/src/hwinit/heap.h new file mode 100644 index 0000000..fc711c0 --- /dev/null +++ b/src/hwinit/heap.h @@ -0,0 +1,28 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _HEAP_H_ +#define _HEAP_H_ + +#include "types.h" + +void heap_init(u32 base); +void *malloc(u32 size); +void *realloc(void * ptr, size_t size); +void *calloc(u32 num, u32 size); +void free(void *buf); + +#endif diff --git a/src/hwinit/i2c.c b/src/hwinit/i2c.c new file mode 100644 index 0000000..6d6c1c2 --- /dev/null +++ b/src/hwinit/i2c.c @@ -0,0 +1,132 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include + +#include "i2c.h" +#include "util.h" + +static u32 i2c_addrs[] = { 0x7000C000, 0x7000C400, 0x7000C500, 0x7000C700, 0x7000D000, 0x7000D100 }; + +static void _i2c_wait(vu32 *base) +{ + base[0x23] = 0x25; + for (u32 i = 0; i < 20; i++) + { + sleep(1); + if (!(base[0x23] & 1)) + break; + } +} + +static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size) +{ + if (size > 4) + return 0; + + u32 tmp = 0; + memcpy(&tmp, buf, size); + + vu32 *base = (vu32 *)i2c_addrs[idx]; + base[1] = x << 1; //Set x (send mode). + base[3] = tmp; //Set value. + base[0] = (2 * size - 2) | 0x2800; //Set size and send mode. + _i2c_wait(base); //Kick transaction. + + base[0] = (base[0] & 0xFFFFFDFF) | 0x200; + while (base[7] & 0x100) + ; + + if (base[7] << 28) + return 0; + + return 1; +} + +static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x) +{ + if (size > 4) + return 0; + + vu32 *base = (vu32 *)i2c_addrs[idx]; + base[1] = (x << 1) | 1; //Set x (recv mode). + base[0] = (2 * size - 2) | 0x2840; //Set size and recv mode. + _i2c_wait(base); //Kick transaction. + + base[0] = (base[0] & 0xFFFFFDFF) | 0x200; + while (base[7] & 0x100) + ; + + if (base[7] << 28) + return 0; + + u32 tmp = base[3]; //Get value. + memcpy(buf, &tmp, size); + + return 1; +} + +void i2c_init(u32 idx) +{ + vu32 *base = (vu32 *)i2c_addrs[idx]; + + base[0x1B] = 0x50001; + base[0x21] = 0x90003; + _i2c_wait(base); + + for (u32 i = 0; i < 10; i++) + { + sleep(20000); + if (base[0x1A] & 0x800) + break; + } + + (vu32)base[0x22]; + base[0x1A] = base[0x1A]; +} + +int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size) +{ + u8 tmp[4]; + + if (size > 3) + return 0; + + tmp[0] = y; + memcpy(tmp + 1, buf, size); + + return _i2c_send_pkt(idx, x, tmp, size + 1); +} + +int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y) +{ + int res = _i2c_send_pkt(idx, x, (u8 *)&y, 1); + if (res) + res = _i2c_recv_pkt(idx, buf, size, x); + return res; +} + +int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b) +{ + return i2c_send_buf_small(idx, x, y, &b, 1); +} + +u8 i2c_recv_byte(u32 idx, u32 x, u32 y) +{ + u8 tmp; + i2c_recv_buf_small(&tmp, 1, idx, x, y); + return tmp; +} diff --git a/src/hwinit/i2c.h b/src/hwinit/i2c.h new file mode 100644 index 0000000..699bc48 --- /dev/null +++ b/src/hwinit/i2c.h @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _I2C_H_ +#define _I2C_H_ + +#include "types.h" + +#define I2C_1 0 +#define I2C_2 1 +#define I2C_3 2 +#define I2C_4 3 +#define I2C_5 4 +#define I2C_6 5 + +void i2c_init(u32 idx); +int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size); +int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y); +int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b); +u8 i2c_recv_byte(u32 idx, u32 x, u32 y); + +#endif diff --git a/src/hwinit/ini.c b/src/hwinit/ini.c new file mode 100644 index 0000000..3e4401e --- /dev/null +++ b/src/hwinit/ini.c @@ -0,0 +1,141 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include + +#include "ini.h" +#include "ff.h" +#include "heap.h" + + +static char *_strdup(char *str) +{ + char *res = malloc(strlen(str) + 1); + strcpy(res, str); + return res; +} + +int ini_parse(link_t *dst, char *ini_path) +{ + u32 lblen; + char lbuf[512]; + FIL fp; + ini_sec_t *csec = NULL; + + if (f_open(&fp, ini_path, FA_READ) != FR_OK) + return 0; + + do + { + //Fetch one line. + lbuf[0] = 0; + f_gets(lbuf, 512, &fp); + lblen = strlen(lbuf); + + //Skip empty lines and comments. + if (lblen <= 1 || lbuf[0] == '#') + continue; + + //Remove trailing newline. + if (lbuf[lblen - 1] == '\n') + lbuf[lblen - 1] = 0; + + if (lblen > 2 && lbuf[0] == '[') //Create new section. + { + if (csec) + { + list_append(dst, &csec->link); + csec = NULL; + } + + u32 i; + for (i = 0; i < lblen && lbuf[i] != '\n' && lbuf[i] != ']'; i++) + ; + lbuf[i] = 0; + + csec = (ini_sec_t *)malloc(sizeof(ini_sec_t)); + csec->name = _strdup(&lbuf[1]); + list_init(&csec->kvs); + } + else if (csec) //Extract key/value. + { + u32 i; + for (i = 0; i < lblen && lbuf[i] != '\n' && lbuf[i] != '='; i++) + ; + lbuf[i] = 0; + + ini_kv_t *kv = (ini_kv_t *)malloc(sizeof(ini_kv_t)); + kv->key = _strdup(&lbuf[0]); + kv->val = _strdup(&lbuf[i + 1]); + list_append(&csec->kvs, &kv->link); + } + } while (!f_eof(&fp)); + + f_close(&fp); + + if (csec) + list_append(dst, &csec->link); + + return 1; +} + +void ini_free(link_t *dst) +{ + LIST_FOREACH_ENTRY(ini_sec_t, ini_sec, dst, link) + { + LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) + { + free(kv->key); + free(kv->val); + free(kv); + } + free(ini_sec->name); + free(ini_sec); + } +} + +ini_sec_t *ini_clone_section(ini_sec_t *cfg) +{ + if (cfg == NULL) + return NULL; + + ini_sec_t *csec = (ini_sec_t *)malloc(sizeof(ini_sec_t)); + list_init(&csec->kvs); + + LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg->kvs, link) + { + ini_kv_t *kvcfg = (ini_kv_t *)malloc(sizeof(ini_kv_t)); + kvcfg->key = _strdup(kv->key); + kvcfg->val = _strdup(kv->val); + list_append(&csec->kvs, &kvcfg->link); + } + + return csec; +} + +void ini_free_section(ini_sec_t *cfg) +{ + if (cfg == NULL) + return; + + LIST_FOREACH_ENTRY(ini_kv_t, kv, &cfg->kvs, link) + { + free(kv->key); + free(kv->val); + free(kv); + } + free(cfg); +} diff --git a/src/hwinit/ini.h b/src/hwinit/ini.h new file mode 100644 index 0000000..6f8d692 --- /dev/null +++ b/src/hwinit/ini.h @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _INI_H_ +#define _INI_H_ + +#include "types.h" +#include "list.h" + +typedef struct _ini_kv_t +{ + char *key; + char *val; + link_t link; +} ini_kv_t; + +typedef struct _ini_sec_t +{ + char *name; + link_t kvs; + link_t link; +} ini_sec_t; + +int ini_parse(link_t *dst, char *ini_path); +void ini_free(link_t *dst); +ini_sec_t *ini_clone_section(ini_sec_t *cfg); +void ini_free_section(ini_sec_t *cfg); + +#endif diff --git a/src/hwinit/integer.h b/src/hwinit/integer.h new file mode 100644 index 0000000..4fcf5c4 --- /dev/null +++ b/src/hwinit/integer.h @@ -0,0 +1,38 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef FF_INTEGER +#define FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include +typedef unsigned __int64 QWORD; + + +#else /* Embedded platform */ + +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* This type MUST be 8-bit */ +typedef unsigned char BYTE; + +/* These types MUST be 16-bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types MUST be 32-bit */ +typedef long LONG; +typedef unsigned long DWORD; + +/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ +typedef unsigned long long QWORD; + +#endif + +#endif diff --git a/src/hwinit/kfuse.c b/src/hwinit/kfuse.c new file mode 100644 index 0000000..03d95bb --- /dev/null +++ b/src/hwinit/kfuse.c @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "kfuse.h" +#include "clock.h" +#include "t210.h" + +int kfuse_read(u32 *buf) +{ + int res = 0; + + clock_enable_kfuse(); + + while (!(KFUSE(KFUSE_STATE) & KFUSE_STATE_DONE)) + ; + + if (!(KFUSE(KFUSE_STATE) & KFUSE_STATE_CRCPASS)) + goto out; + + KFUSE(KFUSE_KEYADDR) = KFUSE_KEYADDR_AUTOINC; + for (int i = 0; i < KFUSE_NUM_WORDS; i++) + buf[i] = KFUSE(KFUSE_KEYS); + + res = 1; + +out:; + clock_disable_kfuse(); + return res; +} diff --git a/src/hwinit/kfuse.h b/src/hwinit/kfuse.h new file mode 100644 index 0000000..4aa1b09 --- /dev/null +++ b/src/hwinit/kfuse.h @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _KFUSE_H_ +#define _KFUSE_H_ + +#include "types.h" + +#define KFUSE_STATE_SOFTRESET (1<<31) +#define KFUSE_STATE_STOP (1<<25) +#define KFUSE_STATE_RESTART (1<<24) +#define KFUSE_STATE_CRCPASS (1<<17) +#define KFUSE_STATE_DONE (1<<16) +#define KFUSE_STATE_ERRBLOCK_MASK 0x3F00 +#define KFUSE_STATE_ERRBLOCK_SHIFT 8 +#define KFUSE_STATE_CURBLOCK_MASK 0x3F + +#define KFUSE_KEYADDR_AUTOINC (1<<16) + +#define KFUSE_STATE 0x80 +#define KFUSE_KEYADDR 0x88 +#define KFUSE_KEYS 0x8C + +#define KFUSE_NUM_WORDS 144 + +int kfuse_read(u32 *buf); + +#endif diff --git a/src/hwinit/list.h b/src/hwinit/list.h new file mode 100644 index 0000000..873cbf8 --- /dev/null +++ b/src/hwinit/list.h @@ -0,0 +1,95 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _LIST_H_ +#define _LIST_H_ + +#include "types.h" + +/*! Initialize list. */ +#define LIST_INIT(name) link_t name = {&name, &name} + +/*! Initialize static list. */ +#define LIST_INIT_STATIC(name) static link_t name = {&name, &name} + +/*! Iterate over all list links. */ +#define LIST_FOREACH(iter, list) \ + for(link_t *iter = (list)->next; iter != (list); iter = iter->next) + +/*! Safely iterate over all list links. */ +#define LIST_FOREACH_SAFE(iter, list) \ + for(link_t *iter = (list)->next, *safe = iter->next; iter != (list); iter = safe, safe = iter->next) + +/*! Iterate over all list members. */ +#define LIST_FOREACH_ENTRY(etype, iter, list, mn) \ + for(etype *iter = CONTAINER_OF((list)->next, etype, mn); &iter->mn != (list); iter = CONTAINER_OF(iter->mn.next, etype, mn)) + +typedef struct _link_t +{ + struct _link_t *prev; + struct _link_t *next; +} link_t; + +static inline void link_init(link_t *l) +{ + l->prev = NULL; + l->next = NULL; +} + +static inline int link_used(link_t *l) +{ + if(l->next == NULL) + return 1; + return 0; +} + +static inline void list_init(link_t *lh) +{ + lh->prev = lh; + lh->next = lh; +} + +static inline void list_prepend(link_t *lh, link_t *l) +{ + l->next = lh->next; + l->prev = lh; + lh->next->prev = l; + lh->next = l; +} + +static inline void list_append(link_t *lh, link_t *l) +{ + l->prev = lh->prev; + l->next = lh; + lh->prev->next = l; + lh->prev = l; +} + +static inline void list_remove(link_t *l) +{ + l->next->prev = l->prev; + l->prev->next = l->next; + link_init(l); +} + +static inline int list_empty(link_t *lh) +{ + if(lh->next == lh) + return 1; + return 0; +} + +#endif diff --git a/src/hwinit/lz.c b/src/hwinit/lz.c new file mode 100644 index 0000000..a17c6e4 --- /dev/null +++ b/src/hwinit/lz.c @@ -0,0 +1,179 @@ +/************************************************************************* +* Name: lz.c +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder implementation. +* Reentrant: Yes +* +* The LZ77 compression scheme is a substitutional compression scheme +* proposed by Abraham Lempel and Jakob Ziv in 1977. It is very simple in +* its design, and uses no fancy bit level compression. +* +* This is my first attempt at an implementation of a LZ77 code/decoder. +* +* The principle of the LZ77 compression algorithm is to store repeated +* occurrences of strings as references to previous occurrences of the same +* string. The point is that the reference consumes less space than the +* string itself, provided that the string is long enough (in this +* implementation, the string has to be at least 4 bytes long, since the +* minimum coded reference is 3 bytes long). Also note that the term +* "string" refers to any kind of byte sequence (it does not have to be +* an ASCII string, for instance). +* +* The coder uses a brute force approach to finding string matches in the +* history buffer (or "sliding window", if you wish), which is very, very +* slow. I recon the complexity is somewhere between O(n^2) and O(n^3), +* depending on the input data. +* +* There is also a faster implementation that uses a large working buffer +* in which a "jump table" is stored, which is used to quickly find +* possible string matches (see the source code for LZ_CompressFast() for +* more information). The faster method is an order of magnitude faster, +* but still quite slow compared to other compression methods. +* +* The upside is that decompression is very fast, and the compression ratio +* is often very good. +* +* The reference to a string is coded as a (length,offset) pair, where the +* length indicates the length of the string, and the offset gives the +* offset from the current data position. To distinguish between string +* references and literal strings (uncompressed bytes), a string reference +* is preceded by a marker byte, which is chosen as the least common byte +* symbol in the input data stream (this marker byte is stored in the +* output stream as the first byte). +* +* Occurrences of the marker byte in the stream are encoded as the marker +* byte followed by a zero byte, which means that occurrences of the marker +* byte have to be coded with two bytes. +* +* The lengths and offsets are coded in a variable length fashion, allowing +* values of any magnitude (up to 4294967295 in this implementation). +* +* With this compression scheme, the worst case compression result is +* (257/256)*insize + 1. +* +*------------------------------------------------------------------------- +* Copyright (c) 2003-2006 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + + +/************************************************************************* +* INTERNAL FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* _LZ_ReadVarSize() - Read unsigned integer with variable number of +* bytes depending on value. +*************************************************************************/ + +static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf ) +{ + unsigned int y, b, num_bytes; + + /* Read complete value (stop when byte contains zero in 8:th bit) */ + y = 0; + num_bytes = 0; + do + { + b = (unsigned int) (*buf ++); + y = (y << 7) | (b & 0x0000007f); + ++ num_bytes; + } + while( b & 0x00000080 ); + + /* Store value in x */ + *x = y; + + /* Return number of bytes read */ + return num_bytes; +} + + + +/************************************************************************* +* PUBLIC FUNCTIONS * +*************************************************************************/ + + +/************************************************************************* +* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder. +* in - Input (compressed) buffer. +* out - Output (uncompressed) buffer. This buffer must be large +* enough to hold the uncompressed data. +* insize - Number of input bytes. +*************************************************************************/ + +void LZ_Uncompress( const unsigned char *in, unsigned char *out, + unsigned int insize ) +{ + unsigned char marker, symbol; + unsigned int i, inpos, outpos, length, offset; + + /* Do we have anything to uncompress? */ + if( insize < 1 ) + { + return; + } + + /* Get marker symbol from input stream */ + marker = in[ 0 ]; + inpos = 1; + + /* Main decompression loop */ + outpos = 0; + do + { + symbol = in[ inpos ++ ]; + if( symbol == marker ) + { + /* We had a marker byte */ + if( in[ inpos ] == 0 ) + { + /* It was a single occurrence of the marker byte */ + out[ outpos ++ ] = marker; + ++ inpos; + } + else + { + /* Extract true length and offset */ + inpos += _LZ_ReadVarSize( &length, &in[ inpos ] ); + inpos += _LZ_ReadVarSize( &offset, &in[ inpos ] ); + + /* Copy corresponding data from history window */ + for( i = 0; i < length; ++ i ) + { + out[ outpos ] = out[ outpos - offset ]; + ++ outpos; + } + } + } + else + { + /* No marker, plain copy */ + out[ outpos ++ ] = symbol; + } + } + while( inpos < insize ); +} diff --git a/src/hwinit/lz.h b/src/hwinit/lz.h new file mode 100644 index 0000000..6f31b4a --- /dev/null +++ b/src/hwinit/lz.h @@ -0,0 +1,52 @@ +/************************************************************************* +* Name: lz.h +* Author: Marcus Geelnard +* Description: LZ77 coder/decoder interface. +* Reentrant: Yes +*------------------------------------------------------------------------- +* Copyright (c) 2003-2006 Marcus Geelnard +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would +* be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not +* be misrepresented as being the original software. +* +* 3. This notice may not be removed or altered from any source +* distribution. +* +* Marcus Geelnard +* marcus.geelnard at home.se +*************************************************************************/ + +#ifndef _lz_h_ +#define _lz_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* +* Function prototypes +*************************************************************************/ + +void LZ_Uncompress( const unsigned char *in, unsigned char *out, + unsigned int insize ); + + +#ifdef __cplusplus +} +#endif + +#endif /* _lz_h_ */ diff --git a/src/hwinit/max77620.h b/src/hwinit/max77620.h new file mode 100644 index 0000000..7223067 --- /dev/null +++ b/src/hwinit/max77620.h @@ -0,0 +1,324 @@ +/* + * Defining registers address and its bit definitions of MAX77620 and MAX20024 + * + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _MFD_MAX77620_H_ +#define _MFD_MAX77620_H_ + +/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ +#define MAX77620_REG_CNFGGLBL1 0x00 +#define MAX77620_REG_CNFGGLBL2 0x01 +#define MAX77620_REG_CNFGGLBL3 0x02 +#define MAX77620_REG_CNFG1_32K 0x03 +#define MAX77620_REG_CNFGBBC 0x04 +#define MAX77620_REG_IRQTOP 0x05 +#define MAX77620_REG_INTLBT 0x06 +#define MAX77620_REG_IRQSD 0x07 +#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 +#define MAX77620_REG_IRQ_LVL2_L8 0x09 +#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A +#define MAX77620_REG_ONOFFIRQ 0x0B +#define MAX77620_REG_NVERC 0x0C +#define MAX77620_REG_IRQTOPM 0x0D +#define MAX77620_REG_INTENLBT 0x0E +#define MAX77620_REG_IRQMASKSD 0x0F +#define MAX77620_REG_IRQ_MSK_L0_7 0x10 +#define MAX77620_REG_IRQ_MSK_L8 0x11 +#define MAX77620_REG_ONOFFIRQM 0x12 +#define MAX77620_REG_STATLBT 0x13 +#define MAX77620_REG_STATSD 0x14 +#define MAX77620_REG_ONOFFSTAT 0x15 + +/* SD and LDO Registers */ +#define MAX77620_REG_SD0 0x16 +#define MAX77620_REG_SD1 0x17 +#define MAX77620_REG_SD2 0x18 +#define MAX77620_REG_SD3 0x19 +#define MAX77620_REG_SD4 0x1A +#define MAX77620_REG_DVSSD0 0x1B +#define MAX77620_REG_DVSSD1 0x1C +#define MAX77620_REG_SD0_CFG 0x1D +#define MAX77620_REG_SD1_CFG 0x1E +#define MAX77620_REG_SD2_CFG 0x1F +#define MAX77620_REG_SD3_CFG 0x20 +#define MAX77620_REG_SD4_CFG 0x21 +#define MAX77620_REG_SD_CFG2 0x22 +#define MAX77620_REG_LDO0_CFG 0x23 +#define MAX77620_REG_LDO0_CFG2 0x24 +#define MAX77620_REG_LDO1_CFG 0x25 +#define MAX77620_REG_LDO1_CFG2 0x26 +#define MAX77620_REG_LDO2_CFG 0x27 +#define MAX77620_REG_LDO2_CFG2 0x28 +#define MAX77620_REG_LDO3_CFG 0x29 +#define MAX77620_REG_LDO3_CFG2 0x2A +#define MAX77620_REG_LDO4_CFG 0x2B +#define MAX77620_REG_LDO4_CFG2 0x2C +#define MAX77620_REG_LDO5_CFG 0x2D +#define MAX77620_REG_LDO5_CFG2 0x2E +#define MAX77620_REG_LDO6_CFG 0x2F +#define MAX77620_REG_LDO6_CFG2 0x30 +#define MAX77620_REG_LDO7_CFG 0x31 +#define MAX77620_REG_LDO7_CFG2 0x32 +#define MAX77620_REG_LDO8_CFG 0x33 +#define MAX77620_REG_LDO8_CFG2 0x34 +#define MAX77620_REG_LDO_CFG3 0x35 + +#define MAX77620_LDO_SLEW_RATE_MASK 0x1 + +/* LDO Configuration 3 */ +#define MAX77620_TRACK4_MASK (1 << 5) +#define MAX77620_TRACK4_SHIFT 5 + +/* Voltage */ +#define MAX77620_SDX_VOLT_MASK 0xFF +#define MAX77620_SD0_VOLT_MASK 0x3F +#define MAX77620_SD1_VOLT_MASK 0x7F +#define MAX77620_LDO_VOLT_MASK 0x3F + +#define MAX77620_REG_GPIO0 0x36 +#define MAX77620_REG_GPIO1 0x37 +#define MAX77620_REG_GPIO2 0x38 +#define MAX77620_REG_GPIO3 0x39 +#define MAX77620_REG_GPIO4 0x3A +#define MAX77620_REG_GPIO5 0x3B +#define MAX77620_REG_GPIO6 0x3C +#define MAX77620_REG_GPIO7 0x3D +#define MAX77620_REG_PUE_GPIO 0x3E +#define MAX77620_REG_PDE_GPIO 0x3F +#define MAX77620_REG_AME_GPIO 0x40 +#define MAX77620_REG_ONOFFCNFG1 0x41 +#define MAX77620_REG_ONOFFCNFG2 0x42 + +/* FPS Registers */ +#define MAX77620_REG_FPS_CFG0 0x43 +#define MAX77620_REG_FPS_CFG1 0x44 +#define MAX77620_REG_FPS_CFG2 0x45 +#define MAX77620_REG_FPS_LDO0 0x46 +#define MAX77620_REG_FPS_LDO1 0x47 +#define MAX77620_REG_FPS_LDO2 0x48 +#define MAX77620_REG_FPS_LDO3 0x49 +#define MAX77620_REG_FPS_LDO4 0x4A +#define MAX77620_REG_FPS_LDO5 0x4B +#define MAX77620_REG_FPS_LDO6 0x4C +#define MAX77620_REG_FPS_LDO7 0x4D +#define MAX77620_REG_FPS_LDO8 0x4E +#define MAX77620_REG_FPS_SD0 0x4F +#define MAX77620_REG_FPS_SD1 0x50 +#define MAX77620_REG_FPS_SD2 0x51 +#define MAX77620_REG_FPS_SD3 0x52 +#define MAX77620_REG_FPS_SD4 0x53 +#define MAX77620_REG_FPS_NONE 0 + +#define MAX77620_FPS_SRC_MASK 0xC0 +#define MAX77620_FPS_SRC_SHIFT 6 +#define MAX77620_FPS_PU_PERIOD_MASK 0x38 +#define MAX77620_FPS_PU_PERIOD_SHIFT 3 +#define MAX77620_FPS_PD_PERIOD_MASK 0x07 +#define MAX77620_FPS_PD_PERIOD_SHIFT 0 +#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 +#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 +#define MAX77620_FPS_EN_SRC_MASK 0x06 +#define MAX77620_FPS_EN_SRC_SHIFT 1 +#define MAX77620_FPS_ENFPS_SW_MASK 0x01 +#define MAX77620_FPS_ENFPS_SW 0x01 + +/* Minimum and maximum FPS period time (in microseconds) are + * different for MAX77620 and Max20024. + */ +#define MAX77620_FPS_PERIOD_MIN_US 40 +#define MAX20024_FPS_PERIOD_MIN_US 20 + +#define MAX77620_FPS_PERIOD_MAX_US 2560 +#define MAX20024_FPS_PERIOD_MAX_US 5120 + +#define MAX77620_REG_FPS_GPIO1 0x54 +#define MAX77620_REG_FPS_GPIO2 0x55 +#define MAX77620_REG_FPS_GPIO3 0x56 +#define MAX77620_REG_FPS_RSO 0x57 +#define MAX77620_REG_CID0 0x58 +#define MAX77620_REG_CID1 0x59 +#define MAX77620_REG_CID2 0x5A +#define MAX77620_REG_CID3 0x5B +#define MAX77620_REG_CID4 0x5C +#define MAX77620_REG_CID5 0x5D + +#define MAX77620_REG_DVSSD4 0x5E +#define MAX20024_REG_MAX_ADD 0x70 + +#define MAX77620_CID_DIDM_MASK 0xF0 +#define MAX77620_CID_DIDM_SHIFT 4 + +/* CNCG2SD */ +#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) +#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) + +/* Device Identification Metal */ +#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) +/* Device Indentification OTP */ +#define MAX77620_CID5_DIDO(n) ((n) & 0xF) + +/* SD CNFG1 */ +#define MAX77620_SD_SR_MASK 0xC0 +#define MAX77620_SD_SR_SHIFT 6 +#define MAX77620_SD_POWER_MODE_MASK 0x30 +#define MAX77620_SD_POWER_MODE_SHIFT 4 +#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) +#define MAX77620_SD_CFG1_ADE_DISABLE 0 +#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) +#define MAX77620_SD_FPWM_MASK 0x04 +#define MAX77620_SD_FPWM_SHIFT 2 +#define MAX77620_SD_FSRADE_MASK 0x01 +#define MAX77620_SD_FSRADE_SHIFT 0 +#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) +#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 +#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) +#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) +#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) +#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 +#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) + +/* LDO_CNFG2 */ +#define MAX77620_LDO_POWER_MODE_MASK 0xC0 +#define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2) +#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1) +#define MAX77620_LDO_CFG2_ADE_DISABLE 0 +#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1) +#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) +#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) +#define MAX77620_LDO_CFG2_SS_SLOW 0 + +#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) +#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) +#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) +#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) +#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) +#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) +#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) + +#define MAX77620_IRQ_LBM_MASK (1 << 3) +#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) +#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) + +#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN 0 +#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_OUTPUT 0 +#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW 0 +#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4) +#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4) +#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5) +#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6) +#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6) +#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6) +#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6) +#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6) + +#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) +#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) +#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) +#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) +#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) +#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) +#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) +#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) + +#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) + +#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) +#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 +#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 +#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) +#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) +#define MAX20024_ONOFFCNFG1_CLRSE 0x18 + +#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) +#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) +#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) +#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) +#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) + +#define MAX77620_GLBLM_MASK (1 << 0) + +#define MAX77620_WDTC_MASK 0x3 +#define MAX77620_WDTOFFC (1 << 4) +#define MAX77620_WDTSLPC (1 << 3) +#define MAX77620_WDTEN (1 << 2) + +#define MAX77620_TWD_MASK 0x3 +#define MAX77620_TWD_2s 0x0 +#define MAX77620_TWD_16s 0x1 +#define MAX77620_TWD_64s 0x2 +#define MAX77620_TWD_128s 0x3 + +#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7) +#define MAX77620_CNFGGLBL1_MPPLD (1 << 6) +#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4)) +#define MAX77620_CNFGGLBL1_LBDAC 0x0E +#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0) + +/* CNFG BBC registers */ +#define MAX77620_CNFGBBC_ENABLE (1 << 0) +#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 +#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 +#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 +#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 +#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) +#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 +#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 + +#define MAX77620_FPS_COUNT 3 + +/* Interrupts */ +enum { + MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ + MAX77620_IRQ_TOP_SD, /* SD power fail */ + MAX77620_IRQ_TOP_LDO, /* LDO power fail */ + MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ + MAX77620_IRQ_TOP_RTC, /* RTC */ + MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ + MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ + MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ +}; + +/* GPIOs */ +enum { + MAX77620_GPIO0, + MAX77620_GPIO1, + MAX77620_GPIO2, + MAX77620_GPIO3, + MAX77620_GPIO4, + MAX77620_GPIO5, + MAX77620_GPIO6, + MAX77620_GPIO7, + MAX77620_GPIO_NR, +}; + +/* FPS Source */ +enum max77620_fps_src { + MAX77620_FPS_SRC_0, + MAX77620_FPS_SRC_1, + MAX77620_FPS_SRC_2, + MAX77620_FPS_SRC_NONE, + MAX77620_FPS_SRC_DEF, +}; + +enum max77620_chip_id { + MAX77620, + MAX20024, +}; + +#endif /* _MFD_MAX77620_H_ */ diff --git a/src/hwinit/max7762x.c b/src/hwinit/max7762x.c new file mode 100644 index 0000000..5a549ed --- /dev/null +++ b/src/hwinit/max7762x.c @@ -0,0 +1,141 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "max7762x.h" +#include "max77620.h" +#include "i2c.h" +#include "util.h" + +#include "gfx.h" +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; +#define DPRINTF(...) gfx_printf(&gfx_con, __VA_ARGS__) + +#define REGULATOR_SD 0 +#define REGULATOR_LDO 1 + +typedef struct _max77620_regulator_t +{ + u8 type; + const char *name; + u8 reg_sd; + u32 mv_step; + u32 mv_min; + u32 mv_default; + u32 mv_max; + u8 volt_addr; + u8 cfg_addr; + u8 volt_mask; + u8 enable_mask; + u8 enable_shift; + u8 status_mask; + + u8 fps_addr; + u8 fps_src; + u8 pd_period; + u8 pu_period; +} max77620_regulator_t; + +static const max77620_regulator_t _pmic_regulators[] = { + { REGULATOR_SD, "sd0", 0x16, 12500, 600000, 625000, 1400000, MAX77620_REG_SD0, MAX77620_REG_SD0_CFG, 0x3F, 0x30, 4, 0x80, 0x4F, 1, 7, 1 }, + { REGULATOR_SD, "sd1", 0x17, 12500, 600000, 1125000, 1125000, MAX77620_REG_SD1, MAX77620_REG_SD1_CFG, 0x3F, 0x30, 4, 0x40, 0x50, 0, 1, 5 }, + { REGULATOR_SD, "sd2", 0x18, 12500, 600000, 1325000, 1350000, MAX77620_REG_SD2, MAX77620_REG_SD2_CFG, 0xFF, 0x30, 4, 0x20, 0x51, 1, 5, 2 }, + { REGULATOR_SD, "sd3", 0x19, 12500, 600000, 1800000, 1800000, MAX77620_REG_SD3, MAX77620_REG_SD3_CFG, 0xFF, 0x30, 4, 0x10, 0x52, 0, 3, 3 }, + { REGULATOR_LDO, "ldo0", 0x00, 25000, 800000, 1200000, 1200000, MAX77620_REG_LDO0_CFG, MAX77620_REG_LDO0_CFG2, 0x3F, 0xC0, 6, 0x00, 0x46, 3, 7, 0 }, + { REGULATOR_LDO, "ldo1", 0x00, 25000, 800000, 1050000, 1050000, MAX77620_REG_LDO1_CFG, MAX77620_REG_LDO1_CFG2, 0x3F, 0xC0, 6, 0x00, 0x47, 3, 7, 0 }, + { REGULATOR_LDO, "ldo2", 0x00, 50000, 800000, 1800000, 3300000, MAX77620_REG_LDO2_CFG, MAX77620_REG_LDO2_CFG2, 0x3F, 0xC0, 6, 0x00, 0x48, 3, 7, 0 }, + { REGULATOR_LDO, "ldo3", 0x00, 50000, 800000, 3100000, 3100000, MAX77620_REG_LDO3_CFG, MAX77620_REG_LDO3_CFG2, 0x3F, 0xC0, 6, 0x00, 0x49, 3, 7, 0 }, + { REGULATOR_LDO, "ldo4", 0x00, 12500, 800000, 850000, 850000, MAX77620_REG_LDO4_CFG, MAX77620_REG_LDO4_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4A, 0, 7, 1 }, + { REGULATOR_LDO, "ldo5", 0x00, 50000, 800000, 1800000, 1800000, MAX77620_REG_LDO5_CFG, MAX77620_REG_LDO5_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4B, 3, 7, 0 }, + { REGULATOR_LDO, "ldo6", 0x00, 50000, 800000, 2900000, 2900000, MAX77620_REG_LDO6_CFG, MAX77620_REG_LDO6_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4C, 3, 7, 0 }, + { REGULATOR_LDO, "ldo7", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO7_CFG, MAX77620_REG_LDO7_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4D, 1, 4, 3 }, + { REGULATOR_LDO, "ldo8", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO8_CFG, MAX77620_REG_LDO8_CFG2, 0x3F, 0xC0, 6, 0x00, 0x4E, 3, 7, 0 } +}; + +int max77620_regulator_get_status(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (reg->type == REGULATOR_SD) + return (i2c_recv_byte(I2C_5, 0x3C, MAX77620_REG_STATSD) & reg->status_mask) ? 0 : 1; + return (i2c_recv_byte(I2C_5, 0x3C, reg->cfg_addr) & 8) ? 1 : 0; +} + +int max77620_regulator_config_fps(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + i2c_send_byte(I2C_5, 0x3C, reg->fps_addr, (reg->fps_src << 6) | (reg->pu_period << 3) | (reg->pd_period)); + + return 1; +} + +int max77620_regulator_set_voltage(u32 id, u32 mv) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (mv < reg->mv_default || mv > reg->mv_max) + return 0; + + u32 mult = (mv + reg->mv_step - 1 - reg->mv_min) / reg->mv_step; + u8 val = i2c_recv_byte(I2C_5, 0x3C, reg->volt_addr); + val = (val & ~reg->volt_mask) | (mult & reg->volt_mask); + i2c_send_byte(I2C_5, 0x3C, reg->volt_addr, val); + sleep(1000); + + return 1; +} + +int max77620_regulator_enable(u32 id, int enable) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + u32 addr = reg->type == REGULATOR_SD ? reg->cfg_addr : reg->volt_addr; + u8 val = i2c_recv_byte(I2C_5, 0x3C, addr); + if (enable) + val = (val & ~reg->enable_mask) | ((3 << reg->enable_shift) & reg->enable_mask); + else + val &= ~reg->enable_mask; + i2c_send_byte(I2C_5, 0x3C, addr, val); + sleep(1000); + + return 1; +} + +void max77620_config_default() +{ + for (u32 i = 1; i <= REGULATOR_MAX; i++) + { + i2c_recv_byte(I2C_5, 0x3C, MAX77620_REG_CID4); + max77620_regulator_config_fps(i); + max77620_regulator_set_voltage(i, _pmic_regulators[i].mv_default); + if (_pmic_regulators[i].fps_src != MAX77620_FPS_SRC_NONE) + max77620_regulator_enable(i, 1); + } + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_SD_CFG2, 4); +} diff --git a/src/hwinit/max7762x.h b/src/hwinit/max7762x.h new file mode 100644 index 0000000..0543495 --- /dev/null +++ b/src/hwinit/max7762x.h @@ -0,0 +1,68 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _MAX7762X_H_ +#define _MAX7762X_H_ + +#include "types.h" + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | | 50000 | 800000 | 3100000 | 3100000 | +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | +* ldo5 | | 50000 | 800000 | 1800000 | 1800000 | +* ldo6 | | 50000 | 800000 | 2900000 | 2900000 | +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | +* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 | +*/ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +/*! MAX77620 partitions. */ +#define REGULATOR_SD0 0 +#define REGULATOR_SD1 1 +#define REGULATOR_SD2 2 +#define REGULATOR_SD3 3 +#define REGULATOR_LDO0 4 +#define REGULATOR_LDO1 5 +#define REGULATOR_LDO2 6 +#define REGULATOR_LDO3 7 +#define REGULATOR_LDO4 8 +#define REGULATOR_LDO5 9 +#define REGULATOR_LDO6 10 +#define REGULATOR_LDO7 11 +#define REGULATOR_LDO8 12 +#define REGULATOR_MAX 12 + +int max77620_regulator_get_status(u32 id); +int max77620_regulator_config_fps(u32 id); +int max77620_regulator_set_voltage(u32 id, u32 mv); +int max77620_regulator_enable(u32 id, int enable); +void max77620_config_default(); + +#endif diff --git a/src/hwinit/mc.c b/src/hwinit/mc.c new file mode 100644 index 0000000..a4cec19 --- /dev/null +++ b/src/hwinit/mc.c @@ -0,0 +1,136 @@ +#include "mc.h" +#include "t210.h" +#include "clock.h" +#include "util.h" + +void mc_config_tsec_carveout(u32 bom, u32 size1mb, int lock) +{ + MC(MC_SEC_CARVEOUT_BOM) = bom; + MC(MC_SEC_CARVEOUT_SIZE_MB) = size1mb; + if (lock) + MC(MC_SEC_CARVEOUT_REG_CTRL) = 1; +} + +void mc_config_carveout() +{ + *(vu32 *)0x8005FFFC = 0xC0EDBBCC; + MC(MC_VIDEO_PROTECT_GPU_OVERRIDE_0) = 1; + MC(MC_VIDEO_PROTECT_GPU_OVERRIDE_1) = 0; + MC(MC_VIDEO_PROTECT_BOM) = 0; + MC(MC_VIDEO_PROTECT_SIZE_MB) = 0; + MC(MC_VIDEO_PROTECT_REG_CTRL) = 1; + + //Configure TSEC carveout @ 0x90000000, 1MB. + //mc_config_tsec_carveout(0x90000000, 1, 0); + mc_config_tsec_carveout(0, 0, 1); + + MC(MC_MTS_CARVEOUT_BOM) = 0; + MC(MC_MTS_CARVEOUT_SIZE_MB) = 0; + MC(MC_MTS_CARVEOUT_ADR_HI) = 0; + MC(MC_MTS_CARVEOUT_REG_CTRL) = 1; + MC(MC_SECURITY_CARVEOUT1_BOM) = 0; + MC(MC_SECURITY_CARVEOUT1_BOM_HI) = 0; + MC(MC_SECURITY_CARVEOUT1_SIZE_128KB) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT1_CFG0) = 0x4000006; + MC(MC_SECURITY_CARVEOUT2_BOM) = 0x80020000; + MC(MC_SECURITY_CARVEOUT2_BOM_HI) = 0; + MC(MC_SECURITY_CARVEOUT2_SIZE_128KB) = 2; + MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2) = 0x3000000; + MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4) = 0x300; + MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT2_CFG0) = 0x440167E; + MC(MC_SECURITY_CARVEOUT3_BOM) = 0; + MC(MC_SECURITY_CARVEOUT3_BOM_HI) = 0; + MC(MC_SECURITY_CARVEOUT3_SIZE_128KB) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2) = 0x3000000; + MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4) = 0x300; + MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT3_CFG0) = 0x4401E7E; + MC(MC_SECURITY_CARVEOUT4_BOM) = 0; + MC(MC_SECURITY_CARVEOUT4_BOM_HI) = 0; + MC(MC_SECURITY_CARVEOUT4_SIZE_128KB) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT4_CFG0) = 0x8F; + MC(MC_SECURITY_CARVEOUT5_BOM) = 0; + MC(MC_SECURITY_CARVEOUT5_BOM_HI) = 0; + MC(MC_SECURITY_CARVEOUT5_SIZE_128KB) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; + MC(MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; + MC(MC_SECURITY_CARVEOUT5_CFG0) = 0x8F; +} + +void mc_enable_ahb_redirect() +{ + CLOCK(0x3A4) = (CLOCK(0x3A4) & 0xFFF7FFFF) | 0x80000; + //MC(MC_IRAM_REG_CTRL) &= 0xFFFFFFFE; + MC(MC_IRAM_BOM) = 0x40000000; + MC(MC_IRAM_TOM) = 0x4003F000; +} + +void mc_disable_ahb_redirect() +{ + MC(MC_IRAM_BOM) = 0xFFFFF000; + MC(MC_IRAM_TOM) = 0; + //Disable IRAM_CFG_WRITE_ACCESS (sticky). + //MC(MC_IRAM_REG_CTRL) = MC(MC_IRAM_REG_CTRL) & 0xFFFFFFFE | 1; + CLOCK(0x3A4) &= 0xFFF7FFFF; +} + +void mc_enable() +{ + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) & 0x1FFFFFFF) | 0x40000000; + //Enable MIPI CAL clock. + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = (CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) & 0xFDFFFFFF) | 0x2000000; + //Enable MC clock. + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = (CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) & 0xFFFFFFFE) | 1; + //Enable EMC DLL clock. + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) = (CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) & 0xFFFFBFFF) | 0x4000; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_SET) = 0x2000001; //Clear EMC and MC reset. + sleep(5); + + //#ifdef CONFIG_ENABLE_AHB_REDIRECT + mc_disable_ahb_redirect(); + //mc_enable_ahb_redirect(); + //#endif +} diff --git a/src/hwinit/mc.h b/src/hwinit/mc.h new file mode 100644 index 0000000..536b5dc --- /dev/null +++ b/src/hwinit/mc.h @@ -0,0 +1,13 @@ +#ifndef _MC_H_ +#define _MC_H_ + +#include "types.h" +#include "mc_t210.h" + +void mc_config_tsec_carveout(u32 bom, u32 size1mb, int lock); +void mc_config_carveout(); +void mc_enable_ahb_redirect(); +void mc_disable_ahb_redirect(); +void mc_enable(); + +#endif diff --git a/src/hwinit/mc_t210.h b/src/hwinit/mc_t210.h new file mode 100644 index 0000000..4f696a5 --- /dev/null +++ b/src/hwinit/mc_t210.h @@ -0,0 +1,466 @@ +/* +* Copyright (c) 2014, NVIDIA Corporation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef _MC_T210_H_ +#define _MC_T210_H_ + +#define MC_INTSTATUS 0x0 +#define MC_INTMASK 0x4 +#define MC_ERR_STATUS 0x8 +#define MC_ERR_ADR 0xc +#define MC_PCFIFO_CLIENT_CONFIG0 0xdd0 +#define MC_PCFIFO_CLIENT_CONFIG1 0xdd4 +#define MC_PCFIFO_CLIENT_CONFIG2 0xdd8 +#define MC_PCFIFO_CLIENT_CONFIG3 0xddc +#define MC_PCFIFO_CLIENT_CONFIG4 0xde0 +#define MC_EMEM_CFG 0x50 +#define MC_EMEM_ADR_CFG 0x54 +#define MC_EMEM_ADR_CFG_DEV0 0x58 +#define MC_EMEM_ADR_CFG_DEV1 0x5c +#define MC_EMEM_ADR_CFG_CHANNEL_MASK 0x60 +#define MC_EMEM_ADR_CFG_BANK_MASK_0 0x64 +#define MC_EMEM_ADR_CFG_BANK_MASK_1 0x68 +#define MC_EMEM_ADR_CFG_BANK_MASK_2 0x6c +#define MC_SECURITY_CFG0 0x70 +#define MC_SECURITY_CFG1 0x74 +#define MC_SECURITY_CFG3 0x9bc +#define MC_SECURITY_RSV 0x7c +#define MC_EMEM_ARB_CFG 0x90 +#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94 +#define MC_EMEM_ARB_TIMING_RCD 0x98 +#define MC_EMEM_ARB_TIMING_RP 0x9c +#define MC_EMEM_ARB_TIMING_RC 0xa0 +#define MC_EMEM_ARB_TIMING_RAS 0xa4 +#define MC_EMEM_ARB_TIMING_FAW 0xa8 +#define MC_EMEM_ARB_TIMING_RRD 0xac +#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0 +#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4 +#define MC_EMEM_ARB_TIMING_R2R 0xb8 +#define MC_EMEM_ARB_TIMING_W2W 0xbc +#define MC_EMEM_ARB_TIMING_R2W 0xc0 +#define MC_EMEM_ARB_TIMING_W2R 0xc4 +#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0 +#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4 +#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0 +#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4 +#define MC_EMEM_ARB_DA_TURNS 0xd0 +#define MC_EMEM_ARB_DA_COVERS 0xd4 +#define MC_EMEM_ARB_MISC0 0xd8 +#define MC_EMEM_ARB_MISC1 0xdc +#define MC_EMEM_ARB_MISC2 0xc8 +#define MC_EMEM_ARB_RING1_THROTTLE 0xe0 +#define MC_EMEM_ARB_RING3_THROTTLE 0xe4 +#define MC_EMEM_ARB_NISO_THROTTLE 0x6b0 +#define MC_EMEM_ARB_OVERRIDE 0xe8 +#define MC_EMEM_ARB_RSV 0xec +#define MC_CLKEN_OVERRIDE 0xf4 +#define MC_TIMING_CONTROL_DBG 0xf8 +#define MC_TIMING_CONTROL 0xfc +#define MC_STAT_CONTROL 0x100 +#define MC_STAT_STATUS 0x104 +#define MC_STAT_EMC_CLOCK_LIMIT 0x108 +#define MC_STAT_EMC_CLOCK_LIMIT_MSBS 0x10c +#define MC_STAT_EMC_CLOCKS 0x110 +#define MC_STAT_EMC_CLOCKS_MSBS 0x114 +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO 0x118 +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO 0x158 +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI 0x11c +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI 0x15c +#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER 0xa20 +#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER 0xa24 +#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_LO 0x198 +#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_LO 0x1a8 +#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_HI 0x19c +#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_HI 0x1ac +#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_UPPER 0xa28 +#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_UPPER 0xa2c +#define MC_STAT_EMC_FILTER_SET0_ASID 0x1a0 +#define MC_STAT_EMC_FILTER_SET1_ASID 0x1b0 +#define MC_STAT_EMC_FILTER_SET0_SLACK_LIMIT 0x120 +#define MC_STAT_EMC_FILTER_SET1_SLACK_LIMIT 0x160 +#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 0x128 +#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 0x168 +#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 0x12c +#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 0x16c +#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 0x130 +#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 0x170 +#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 0x134 +#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 0xb88 +#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 0x174 +#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 0xb8c +#define MC_STAT_EMC_SET0_COUNT 0x138 +#define MC_STAT_EMC_SET0_COUNT_MSBS 0x13c +#define MC_STAT_EMC_SET1_COUNT 0x178 +#define MC_STAT_EMC_SET1_COUNT_MSBS 0x17c +#define MC_STAT_EMC_SET0_SLACK_ACCUM 0x140 +#define MC_STAT_EMC_SET0_SLACK_ACCUM_MSBS 0x144 +#define MC_STAT_EMC_SET1_SLACK_ACCUM 0x180 +#define MC_STAT_EMC_SET1_SLACK_ACCUM_MSBS 0x184 +#define MC_STAT_EMC_SET0_HISTO_COUNT 0x148 +#define MC_STAT_EMC_SET0_HISTO_COUNT_MSBS 0x14c +#define MC_STAT_EMC_SET1_HISTO_COUNT 0x188 +#define MC_STAT_EMC_SET1_HISTO_COUNT_MSBS 0x18c +#define MC_STAT_EMC_SET0_MINIMUM_SLACK_OBSERVED 0x150 +#define MC_STAT_EMC_SET1_MINIMUM_SLACK_OBSERVED 0x190 +#define MC_STAT_EMC_SET0_IDLE_CYCLE_COUNT 0x1b8 +#define MC_STAT_EMC_SET0_IDLE_CYCL_COUNT_MSBS 0x1bc +#define MC_STAT_EMC_SET1_IDLE_CYCLE_COUNT 0x1c8 +#define MC_STAT_EMC_SET1_IDLE_CYCL_COUNT_MSBS 0x1cc +#define MC_STAT_EMC_SET0_IDLE_CYCLE_PARTITION_SELECT 0x1c0 +#define MC_STAT_EMC_SET1_IDLE_CYCLE_PARTITION_SELECT 0x1d0 +#define MC_CLIENT_HOTRESET_CTRL 0x200 +#define MC_CLIENT_HOTRESET_CTRL_1 0x970 +#define MC_CLIENT_HOTRESET_STATUS 0x204 +#define MC_CLIENT_HOTRESET_STATUS_1 0x974 +#define MC_EMEM_ARB_ISOCHRONOUS_0 0x208 +#define MC_EMEM_ARB_ISOCHRONOUS_1 0x20c +#define MC_EMEM_ARB_ISOCHRONOUS_2 0x210 +#define MC_EMEM_ARB_ISOCHRONOUS_3 0x214 +#define MC_EMEM_ARB_ISOCHRONOUS_4 0xb94 +#define MC_EMEM_ARB_HYSTERESIS_0 0x218 +#define MC_EMEM_ARB_HYSTERESIS_1 0x21c +#define MC_EMEM_ARB_HYSTERESIS_2 0x220 +#define MC_EMEM_ARB_HYSTERESIS_3 0x224 +#define MC_EMEM_ARB_HYSTERESIS_4 0xb84 +#define MC_EMEM_ARB_DHYSTERESIS_0 0xbb0 +#define MC_EMEM_ARB_DHYSTERESIS_1 0xbb4 +#define MC_EMEM_ARB_DHYSTERESIS_2 0xbb8 +#define MC_EMEM_ARB_DHYSTERESIS_3 0xbbc +#define MC_EMEM_ARB_DHYSTERESIS_4 0xbc0 +#define MC_EMEM_ARB_DHYST_CTRL 0xbcc +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec +#define MC_RESERVED_RSV 0x3fc +#define MC_DISB_EXTRA_SNAP_LEVELS 0x408 +#define MC_APB_EXTRA_SNAP_LEVELS 0x2a4 +#define MC_AHB_EXTRA_SNAP_LEVELS 0x2a0 +#define MC_USBD_EXTRA_SNAP_LEVELS 0xa18 +#define MC_ISP_EXTRA_SNAP_LEVELS 0xa08 +#define MC_AUD_EXTRA_SNAP_LEVELS 0xa10 +#define MC_MSE_EXTRA_SNAP_LEVELS 0x40c +#define MC_GK2_EXTRA_SNAP_LEVELS 0xa40 +#define MC_A9AVPPC_EXTRA_SNAP_LEVELS 0x414 +#define MC_FTOP_EXTRA_SNAP_LEVELS 0x2bc +#define MC_JPG_EXTRA_SNAP_LEVELS 0xa3c +#define MC_HOST_EXTRA_SNAP_LEVELS 0xa14 +#define MC_SAX_EXTRA_SNAP_LEVELS 0x2c0 +#define MC_DIS_EXTRA_SNAP_LEVELS 0x2ac +#define MC_VICPC_EXTRA_SNAP_LEVELS 0xa1c +#define MC_HDAPC_EXTRA_SNAP_LEVELS 0xa48 +#define MC_AVP_EXTRA_SNAP_LEVELS 0x2a8 +#define MC_USBX_EXTRA_SNAP_LEVELS 0x404 +#define MC_PCX_EXTRA_SNAP_LEVELS 0x2b8 +#define MC_SD_EXTRA_SNAP_LEVELS 0xa04 +#define MC_DFD_EXTRA_SNAP_LEVELS 0xa4c +#define MC_VE_EXTRA_SNAP_LEVELS 0x2d8 +#define MC_GK_EXTRA_SNAP_LEVELS 0xa00 +#define MC_VE2_EXTRA_SNAP_LEVELS 0x410 +#define MC_SDM_EXTRA_SNAP_LEVELS 0xa44 +#define MC_VIDEO_PROTECT_BOM 0x648 +#define MC_VIDEO_PROTECT_SIZE_MB 0x64c +#define MC_VIDEO_PROTECT_BOM_ADR_HI 0x978 +#define MC_VIDEO_PROTECT_REG_CTRL 0x650 +#define MC_ERR_VPR_STATUS 0x654 +#define MC_ERR_VPR_ADR 0x658 +#define MC_VIDEO_PROTECT_VPR_OVERRIDE 0x418 +#define MC_VIDEO_PROTECT_VPR_OVERRIDE1 0x590 +#define MC_IRAM_BOM 0x65c +#define MC_IRAM_TOM 0x660 +#define MC_IRAM_ADR_HI 0x980 +#define MC_IRAM_REG_CTRL 0x964 +#define MC_EMEM_CFG_ACCESS_CTRL 0x664 +#define MC_TZ_SECURITY_CTRL 0x668 +#define MC_EMEM_ARB_OUTSTANDING_REQ_RING3 0x66c +#define MC_EMEM_ARB_OUTSTANDING_REQ_NISO 0x6b4 +#define MC_EMEM_ARB_RING0_THROTTLE_MASK 0x6bc +#define MC_EMEM_ARB_NISO_THROTTLE_MASK 0x6b8 +#define MC_EMEM_ARB_NISO_THROTTLE_MASK_1 0xb80 +#define MC_SEC_CARVEOUT_BOM 0x670 +#define MC_SEC_CARVEOUT_SIZE_MB 0x674 +#define MC_SEC_CARVEOUT_ADR_HI 0x9d4 +#define MC_SEC_CARVEOUT_REG_CTRL 0x678 +#define MC_ERR_SEC_STATUS 0x67c +#define MC_ERR_SEC_ADR 0x680 +#define MC_PC_IDLE_CLOCK_GATE_CONFIG 0x684 +#define MC_STUTTER_CONTROL 0x688 +#define MC_RESERVED_RSV_1 0x958 +#define MC_DVFS_PIPE_SELECT 0x95c +#define MC_AHB_PTSA_MIN 0x4e0 +#define MC_AUD_PTSA_MIN 0x54c +#define MC_MLL_MPCORER_PTSA_RATE 0x44c +#define MC_RING2_PTSA_RATE 0x440 +#define MC_USBD_PTSA_RATE 0x530 +#define MC_USBX_PTSA_MIN 0x528 +#define MC_USBD_PTSA_MIN 0x534 +#define MC_APB_PTSA_MAX 0x4f0 +#define MC_JPG_PTSA_RATE 0x584 +#define MC_DIS_PTSA_MIN 0x420 +#define MC_AVP_PTSA_MAX 0x4fc +#define MC_AVP_PTSA_RATE 0x4f4 +#define MC_RING1_PTSA_MIN 0x480 +#define MC_DIS_PTSA_MAX 0x424 +#define MC_SD_PTSA_MAX 0x4d8 +#define MC_MSE_PTSA_RATE 0x4c4 +#define MC_VICPC_PTSA_MIN 0x558 +#define MC_PCX_PTSA_MAX 0x4b4 +#define MC_ISP_PTSA_RATE 0x4a0 +#define MC_A9AVPPC_PTSA_MIN 0x48c +#define MC_RING2_PTSA_MAX 0x448 +#define MC_AUD_PTSA_RATE 0x548 +#define MC_HOST_PTSA_MIN 0x51c +#define MC_MLL_MPCORER_PTSA_MAX 0x454 +#define MC_SD_PTSA_MIN 0x4d4 +#define MC_RING1_PTSA_RATE 0x47c +#define MC_JPG_PTSA_MIN 0x588 +#define MC_HDAPC_PTSA_MIN 0x62c +#define MC_AVP_PTSA_MIN 0x4f8 +#define MC_JPG_PTSA_MAX 0x58c +#define MC_VE_PTSA_MAX 0x43c +#define MC_DFD_PTSA_MAX 0x63c +#define MC_VICPC_PTSA_RATE 0x554 +#define MC_GK_PTSA_MAX 0x544 +#define MC_VICPC_PTSA_MAX 0x55c +#define MC_SDM_PTSA_MAX 0x624 +#define MC_SAX_PTSA_RATE 0x4b8 +#define MC_PCX_PTSA_MIN 0x4b0 +#define MC_APB_PTSA_MIN 0x4ec +#define MC_GK2_PTSA_MIN 0x614 +#define MC_PCX_PTSA_RATE 0x4ac +#define MC_RING1_PTSA_MAX 0x484 +#define MC_HDAPC_PTSA_RATE 0x628 +#define MC_MLL_MPCORER_PTSA_MIN 0x450 +#define MC_GK2_PTSA_MAX 0x618 +#define MC_AUD_PTSA_MAX 0x550 +#define MC_GK2_PTSA_RATE 0x610 +#define MC_ISP_PTSA_MAX 0x4a8 +#define MC_DISB_PTSA_RATE 0x428 +#define MC_VE2_PTSA_MAX 0x49c +#define MC_DFD_PTSA_MIN 0x638 +#define MC_FTOP_PTSA_RATE 0x50c +#define MC_A9AVPPC_PTSA_RATE 0x488 +#define MC_VE2_PTSA_MIN 0x498 +#define MC_USBX_PTSA_MAX 0x52c +#define MC_DIS_PTSA_RATE 0x41c +#define MC_USBD_PTSA_MAX 0x538 +#define MC_A9AVPPC_PTSA_MAX 0x490 +#define MC_USBX_PTSA_RATE 0x524 +#define MC_FTOP_PTSA_MAX 0x514 +#define MC_HDAPC_PTSA_MAX 0x630 +#define MC_SD_PTSA_RATE 0x4d0 +#define MC_DFD_PTSA_RATE 0x634 +#define MC_FTOP_PTSA_MIN 0x510 +#define MC_SDM_PTSA_RATE 0x61c +#define MC_AHB_PTSA_RATE 0x4dc +#define MC_SMMU_SMMU_PTSA_MAX 0x460 +#define MC_RING2_PTSA_MIN 0x444 +#define MC_SDM_PTSA_MIN 0x620 +#define MC_APB_PTSA_RATE 0x4e8 +#define MC_MSE_PTSA_MIN 0x4c8 +#define MC_HOST_PTSA_RATE 0x518 +#define MC_VE_PTSA_RATE 0x434 +#define MC_AHB_PTSA_MAX 0x4e4 +#define MC_SAX_PTSA_MIN 0x4bc +#define MC_SMMU_SMMU_PTSA_MIN 0x45c +#define MC_ISP_PTSA_MIN 0x4a4 +#define MC_HOST_PTSA_MAX 0x520 +#define MC_SAX_PTSA_MAX 0x4c0 +#define MC_VE_PTSA_MIN 0x438 +#define MC_GK_PTSA_MIN 0x540 +#define MC_MSE_PTSA_MAX 0x4cc +#define MC_DISB_PTSA_MAX 0x430 +#define MC_DISB_PTSA_MIN 0x42c +#define MC_SMMU_SMMU_PTSA_RATE 0x458 +#define MC_VE2_PTSA_RATE 0x494 +#define MC_GK_PTSA_RATE 0x53c +#define MC_PTSA_GRANT_DECREMENT 0x960 +#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4 +#define MC_LATENCY_ALLOWANCE_AXIAP_0 0x3a0 +#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380 +#define MC_LATENCY_ALLOWANCE_ISP2B_0 0x384 +#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc +#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8 +#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370 +#define MC_LATENCY_ALLOWANCE_SE_0 0x3e0 +#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374 +#define MC_LATENCY_ALLOWANCE_DC_0 0x2e8 +#define MC_LATENCY_ALLOWANCE_VIC_0 0x394 +#define MC_LATENCY_ALLOWANCE_DCB_1 0x2f8 +#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8 +#define MC_LATENCY_ALLOWANCE_DCB_2 0x2fc +#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390 +#define MC_LATENCY_ALLOWANCE_DC_2 0x2f0 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB 0x694 +#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348 +#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c +#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344 +#define MC_LATENCY_ALLOWANCE_TSECB_0 0x3f0 +#define MC_LATENCY_ALLOWANCE_AFI_0 0x2e0 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B 0x698 +#define MC_LATENCY_ALLOWANCE_DC_1 0x2ec +#define MC_LATENCY_ALLOWANCE_APE_0 0x3dc +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C 0x6a0 +#define MC_LATENCY_ALLOWANCE_A9AVP_0 0x3a4 +#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8 +#define MC_LATENCY_ALLOWANCE_DCB_0 0x2f4 +#define MC_LATENCY_ALLOWANCE_HC_1 0x314 +#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0 +#define MC_LATENCY_ALLOWANCE_NVJPG_0 0x3e4 +#define MC_LATENCY_ALLOWANCE_PTC_0 0x34c +#define MC_LATENCY_ALLOWANCE_ETR_0 0x3ec +#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320 +#define MC_LATENCY_ALLOWANCE_VI2_0 0x398 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB 0x69c +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB 0x6a4 +#define MC_LATENCY_ALLOWANCE_SATA_0 0x350 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A 0x690 +#define MC_LATENCY_ALLOWANCE_HC_0 0x310 +#define MC_LATENCY_ALLOWANCE_DC_3 0x3c8 +#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac +#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4 +#define MC_LATENCY_ALLOWANCE_ISP2B_1 0x388 +#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328 +#define MC_LATENCY_ALLOWANCE_HDA_0 0x318 +#define MC_MIN_LENGTH_APE_0 0xb34 +#define MC_MIN_LENGTH_DCB_2 0x8a8 +#define MC_MIN_LENGTH_A9AVP_0 0x950 +#define MC_MIN_LENGTH_TSEC_0 0x93c +#define MC_MIN_LENGTH_DC_1 0x898 +#define MC_MIN_LENGTH_AXIAP_0 0x94c +#define MC_MIN_LENGTH_ISP2B_0 0x930 +#define MC_MIN_LENGTH_VI2_0 0x944 +#define MC_MIN_LENGTH_DCB_0 0x8a0 +#define MC_MIN_LENGTH_DCB_1 0x8a4 +#define MC_MIN_LENGTH_PPCS_1 0x8f4 +#define MC_MIN_LENGTH_NVJPG_0 0xb3c +#define MC_MIN_LENGTH_HDA_0 0x8c4 +#define MC_MIN_LENGTH_NVENC_0 0x8d4 +#define MC_MIN_LENGTH_SDMMC_0 0xb18 +#define MC_MIN_LENGTH_ISP2B_1 0x934 +#define MC_MIN_LENGTH_HC_1 0x8c0 +#define MC_MIN_LENGTH_DC_3 0xb20 +#define MC_MIN_LENGTH_AVPC_0 0x890 +#define MC_MIN_LENGTH_VIC_0 0x940 +#define MC_MIN_LENGTH_ISP2_0 0x91c +#define MC_MIN_LENGTH_HC_0 0x8bc +#define MC_MIN_LENGTH_SE_0 0xb38 +#define MC_MIN_LENGTH_NVDEC_0 0xb30 +#define MC_MIN_LENGTH_SATA_0 0x8fc +#define MC_MIN_LENGTH_DC_0 0x894 +#define MC_MIN_LENGTH_XUSB_1 0x92c +#define MC_MIN_LENGTH_DC_2 0x89c +#define MC_MIN_LENGTH_SDMMCAA_0 0xb14 +#define MC_MIN_LENGTH_GPU_0 0xb04 +#define MC_MIN_LENGTH_ETR_0 0xb44 +#define MC_MIN_LENGTH_AFI_0 0x88c +#define MC_MIN_LENGTH_PPCS_0 0x8f0 +#define MC_MIN_LENGTH_ISP2_1 0x920 +#define MC_MIN_LENGTH_XUSB_0 0x928 +#define MC_MIN_LENGTH_MPCORE_0 0x8cc +#define MC_MIN_LENGTH_TSECB_0 0xb48 +#define MC_MIN_LENGTH_SDMMCA_0 0xb10 +#define MC_MIN_LENGTH_GPU2_0 0xb40 +#define MC_MIN_LENGTH_SDMMCAB_0 0xb1c +#define MC_MIN_LENGTH_PTC_0 0x8f8 +#define MC_EMEM_ARB_OVERRIDE_1 0x968 +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 0x984 +#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 0x988 +#define MC_EMEM_ARB_STATS_0 0x990 +#define MC_EMEM_ARB_STATS_1 0x994 +#define MC_MTS_CARVEOUT_BOM 0x9a0 +#define MC_MTS_CARVEOUT_SIZE_MB 0x9a4 +#define MC_MTS_CARVEOUT_ADR_HI 0x9a8 +#define MC_MTS_CARVEOUT_REG_CTRL 0x9ac +#define MC_ERR_MTS_STATUS 0x9b0 +#define MC_ERR_MTS_ADR 0x9b4 +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00 +#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04 +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 0xd74 +#define MC_SECURITY_CARVEOUT4_CFG0 0xcf8 +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 0xd10 +#define MC_SECURITY_CARVEOUT4_SIZE_128KB 0xd04 +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 0xc28 +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 0xc30 +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 0xc8c +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 0xd1c +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 0xd70 +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 0xc2c +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 0xd7c +#define MC_SECURITY_CARVEOUT3_SIZE_128KB 0xcb4 +#define MC_SECURITY_CARVEOUT2_CFG0 0xc58 +#define MC_SECURITY_CARVEOUT1_CFG0 0xc08 +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 0xc84 +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 0xc68 +#define MC_SECURITY_CARVEOUT3_BOM 0xcac +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 0xc70 +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 0xd78 +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 0xc7c +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 0xd18 +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 0xcbc +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 0xc38 +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 0xc34 +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 0xcc0 +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 0xd60 +#define MC_SECURITY_CARVEOUT3_CFG0 0xca8 +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 0xcb8 +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 0xc88 +#define MC_SECURITY_CARVEOUT2_SIZE_128KB 0xc64 +#define MC_SECURITY_CARVEOUT5_BOM_HI 0xd50 +#define MC_SECURITY_CARVEOUT1_SIZE_128KB 0xc14 +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 0xd14 +#define MC_SECURITY_CARVEOUT1_BOM 0xc0c +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 0xd2c +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 0xd68 +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 0xcc8 +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 0xd58 +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 0xd24 +#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 0xcc4 +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 0xc78 +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 0xc1c +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 0xc18 +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 0xd28 +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 0xd5c +#define MC_SECURITY_CARVEOUT3_BOM_HI 0xcb0 +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 0xcd8 +#define MC_SECURITY_CARVEOUT2_BOM_HI 0xc60 +#define MC_SECURITY_CARVEOUT4_BOM_HI 0xd00 +#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 0xd64 +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 0xcdc +#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 0xc80 +#define MC_SECURITY_CARVEOUT5_SIZE_128KB 0xd54 +#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 0xd20 +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 0xcd4 +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 0xd0c +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 0xc74 +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 0xccc +#define MC_SECURITY_CARVEOUT4_BOM 0xcfc +#define MC_SECURITY_CARVEOUT5_CFG0 0xd48 +#define MC_SECURITY_CARVEOUT2_BOM 0xc5c +#define MC_SECURITY_CARVEOUT5_BOM 0xd4c +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 0xc24 +#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 0xd6c +#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 0xcd0 +#define MC_SECURITY_CARVEOUT1_BOM_HI 0xc10 +#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 0xc20 +#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 0xc3c +#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 0xc6c +#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 0xd08 +#define MC_ERR_APB_ASID_UPDATE_STATUS 0x9d0 +#define MC_DA_CONFIG0 0x9dc + +#endif diff --git a/src/hwinit/mmc.h b/src/hwinit/mmc.h new file mode 100644 index 0000000..a3beea6 --- /dev/null +++ b/src/hwinit/mmc.h @@ -0,0 +1,432 @@ +/* +* Header for MultiMediaCard (MMC) +* +* Copyright 2002 Hewlett-Packard Company +* +* Use consistent with the GNU GPL is permitted, +* provided that this copyright notice is +* preserved in its entirety in all copies and derived works. +* +* HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, +* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS +* FITNESS FOR ANY PARTICULAR PURPOSE. +* +* Many thanks to Alessandro Rubini and Jonathan Corbet! +* +* Based strongly on code by: +* +* Author: Yong-iL Joh +* +* Author: Andrew Christian +* 15 May 2002 +*/ + +#ifndef LINUX_MMC_MMC_H +#define LINUX_MMC_MMC_H + +/* Standard MMC commands (4.1) type argument response */ +/* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_BUS_TEST_R 14 /* adtc R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ +#define MMC_BUS_TEST_W 19 /* adtc R1 */ +#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */ +#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */ + +/* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ + +/* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + +/* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + +/* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + +/* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 38 /* ac R1b */ + +/* class 9 */ +#define MMC_FAST_IO 39 /* ac R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + +/* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + +/* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ + +/* class 11 */ +#define MMC_QUE_TASK_PARAMS 44 /* ac [20:16] task id R1 */ +#define MMC_QUE_TASK_ADDR 45 /* ac [31:0] data addr R1 */ +#define MMC_EXECUTE_READ_TASK 46 /* adtc [20:16] task id R1 */ +#define MMC_EXECUTE_WRITE_TASK 47 /* adtc [20:16] task id R1 */ +#define MMC_CMDQ_TASK_MGMT 48 /* ac [20:16] task id R1b */ + +/* +* MMC_SWITCH argument format: +* +* [31:26] Always 0 +* [25:24] Access Mode +* [23:16] Location of target Byte in EXT_CSD +* [15:08] Value Byte +* [07:03] Always 0 +* [02:00] Command Set +*/ + +/* +MMC status in R1, for native mode (SPI bits are different) +Type +e : error bit +s : status bit +r : detected and set for the actual command response +x : detected and set during command execution. the host must poll +the card by sending status command in order to read these bits. +Clear condition +a : according to the card state +b : always related to the previous command. Reception of +a valid command will clear it (with a delay of one command) +c : clear by read +*/ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ +#define R1_EXCEPTION_EVENT (1 << 6) /* sr, a */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +/* +* MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS +* R1 is the low order byte; R2 is the next highest byte, when present. +*/ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero */ +#define R2_SPI_CARD_LOCKED (1 << 8) +#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 10) +#define R2_SPI_CC_ERROR (1 << 11) +#define R2_SPI_CARD_ECC_ERROR (1 << 12) +#define R2_SPI_WP_VIOLATION (1 << 13) +#define R2_SPI_ERASE_PARAM (1 << 14) +#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +/* +* OCR bits are mostly in host.h +*/ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +/* +* Card Command Classes (CCC) +*/ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ +/* (CMD0,1,2,3,4,7,9,10,12,13,15) */ +/* (and for SPI, CMD58,59) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ +/* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ +/* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ +/* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ +/* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ +/* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ +/* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ +/* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ +/* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ +/* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ +/* (CMD6,34,35,36,37,50) */ +/* (11) Reserved */ +/* (CMD?) */ + +/* +* CSD field definitions +*/ + +#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ +#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ + +#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ +#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ + +/* +* EXT_CSD fields +*/ + +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ +#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ +#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ +#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ +#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ +#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_HPI_MGMT 161 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_BKOPS_START 164 /* W */ +#define EXT_CSD_SANITIZE_START 165 /* W */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_BOOT_WP 173 /* R/W */ +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */ +#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */ +#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ +#define EXT_CSD_REL_WR_SEC_C 222 /* RO */ +#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_BOOT_MULT 226 /* RO */ +#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ +#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ +#define EXT_CSD_TRIM_MULT 232 /* RO */ +#define EXT_CSD_PWR_CL_200_195 236 /* RO */ +#define EXT_CSD_PWR_CL_200_360 237 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */ +#define EXT_CSD_BKOPS_STATUS 246 /* RO */ +#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ +#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ +#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_DEVICE_VERSION 262 /* RO, 2 bytes */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ +#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ +#define EXT_CSD_SUPPORTED_MODE 493 /* RO */ +#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ +#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ +#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ +#define EXT_CSD_MAX_PACKED_READS 501 /* RO */ +#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ +#define EXT_CSD_HPI_FEATURES 503 /* RO */ + +/* +* EXT_CSD field definitions +*/ + +#define EXT_CSD_WR_REL_PARAM_EN (1<<2) + +#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) +#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) +#define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) +#define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) + +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3) +#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) + +#define EXT_CSD_PART_SETTING_COMPLETED (0x1) +#define EXT_CSD_PART_SUPPORT_PART_EN (0x1) + +#define EXT_CSD_CMD_SET_NORMAL (1<<0) +#define EXT_CSD_CMD_SET_SECURE (1<<1) +#define EXT_CSD_CMD_SET_CPSECURE (1<<2) + +#define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ +#define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ +#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ + EXT_CSD_CARD_TYPE_HS_52) +#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ +/* DDR mode @1.8V or 3V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ +/* DDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ + | EXT_CSD_CARD_TYPE_DDR_1_2V) +#define EXT_CSD_CARD_TYPE_HS200_1_8V (1<<4) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V (1<<5) /* Card can run at 200MHz */ +/* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) +#define EXT_CSD_CARD_TYPE_HS400ES (1<<8) /* Card can run at HS400ES */ + +#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ +#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ +#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ +#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_BUS_WIDTH_STROBE (1<<7) /* Enhanced strobe mode */ + +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ +#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */ + +#define EXT_CSD_SEC_ER_EN (1<<0) +#define EXT_CSD_SEC_BD_BLK_EN (1<<2) +#define EXT_CSD_SEC_GB_CL_EN (1<<4) +#define EXT_CSD_SEC_SANITIZE (1<<6) /* v4.5 only */ + +#define EXT_CSD_RST_N_EN_MASK 0x3 +#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */ + +#define EXT_CSD_NO_POWER_NOTIFICATION 0 +#define EXT_CSD_POWER_ON 1 +#define EXT_CSD_POWER_OFF_SHORT 2 +#define EXT_CSD_POWER_OFF_LONG 3 + +#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 +#define EXT_CSD_PWR_CL_4BIT_SHIFT 0 + +#define EXT_CSD_PACKED_EVENT_EN (1<<3) + +/* +* EXCEPTION_EVENT_STATUS field +*/ +#define EXT_CSD_URGENT_BKOPS (1<<0) +#define EXT_CSD_DYNCAP_NEEDED (1<<1) +#define EXT_CSD_SYSPOOL_EXHAUSTED (1<<2) +#define EXT_CSD_PACKED_FAILURE (1<<3) + +#define EXT_CSD_PACKED_GENERIC_ERROR (1<<0) +#define EXT_CSD_PACKED_INDEXED_ERROR (1<<1) + +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* BKOPS modes +*/ +#define EXT_CSD_MANUAL_BKOPS_MASK 0x01 +#define EXT_CSD_AUTO_BKOPS_MASK 0x02 + +/* +* Command Queue +*/ +#define EXT_CSD_CMDQ_MODE_ENABLED (1<<0) +#define EXT_CSD_CMDQ_DEPTH_MASK 0x1F +#define EXT_CSD_CMDQ_SUPPORTED (1<<0) + +/* +* MMC_SWITCH access modes +*/ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* +* Erase/trim/discard +*/ +#define MMC_ERASE_ARG 0x00000000 +#define MMC_SECURE_ERASE_ARG 0x80000000 +#define MMC_TRIM_ARG 0x00000001 +#define MMC_DISCARD_ARG 0x00000003 +#define MMC_SECURE_TRIM1_ARG 0x80000001 +#define MMC_SECURE_TRIM2_ARG 0x80008000 +#define MMC_SECURE_ARGS 0x80000000 +#define MMC_TRIM_ARGS 0x00008001 + +#endif /* LINUX_MMC_MMC_H */ diff --git a/src/hwinit/nx_emmc.c b/src/hwinit/nx_emmc.c new file mode 100644 index 0000000..fd5e9c7 --- /dev/null +++ b/src/hwinit/nx_emmc.c @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "nx_emmc.h" +#include "heap.h" +#include "list.h" + +void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage) +{ + u8 *buf = (u8 *)malloc(NX_GPT_NUM_BLOCKS * NX_EMMC_BLOCKSIZE); + + sdmmc_storage_read(storage, NX_GPT_FIRST_LBA, NX_GPT_NUM_BLOCKS, buf); + + gpt_header_t *hdr = (gpt_header_t *)buf; + for (u32 i = 0; i < hdr->num_part_ents; i++) + { + gpt_entry_t *ent = (gpt_entry_t *)(buf + (hdr->part_ent_lba - 1) * NX_EMMC_BLOCKSIZE + i * sizeof(gpt_entry_t)); + emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t)); + part->lba_start = ent->lba_start; + part->lba_end = ent->lba_end; + part->attrs = ent->attrs; + + //HACK + for (u32 i = 0; i < 36; i++) + part->name[i] = ent->name[i]; + part->name[36] = 0; + + list_append(gpt, &part->link); + } + + free(buf); +} + +void nx_emmc_gpt_free(link_t *gpt) +{ + LIST_FOREACH_SAFE(iter, gpt) + free(CONTAINER_OF(iter, emmc_part_t, link)); +} + +emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name) +{ + LIST_FOREACH_ENTRY(emmc_part_t, part, gpt, link) + if (!strcmp(part->name, name)) + return part; + return NULL; +} + +int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf) +{ + //The last LBA is inclusive. + if (part->lba_start + sector_off > part->lba_end) + return 0; + return sdmmc_storage_read(storage, part->lba_start + sector_off, num_sectors, buf); +} + +int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf) +{ + //The last LBA is inclusive. + if (part->lba_start + sector_off > part->lba_end) + return 0; + return sdmmc_storage_write(storage, part->lba_start + sector_off, num_sectors, buf); +} diff --git a/src/hwinit/nx_emmc.h b/src/hwinit/nx_emmc.h new file mode 100644 index 0000000..0b81c29 --- /dev/null +++ b/src/hwinit/nx_emmc.h @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _NX_EMMC_H_ +#define _NX_EMMC_H_ + +#include "types.h" +#include "list.h" +#include "sdmmc.h" + +typedef struct _gpt_entry_t +{ + u8 type_guid[0x10]; + u8 part_guid[0x10]; + u64 lba_start; + u64 lba_end; + u64 attrs; + u16 name[36]; +} gpt_entry_t; + +typedef struct _gpt_header_t +{ + u64 signature; + u32 revision; + u32 size; + u32 crc32; + u32 res1; + u64 my_lba; + u64 alt_lba; + u64 first_use_lba; + u64 last_use_lba; + u8 disk_guid[0x10]; + u64 part_ent_lba; + u32 num_part_ents; + u32 part_ent_size; + u32 part_ents_crc32; + u8 res2[420]; +} gpt_header_t; + +#define NX_GPT_FIRST_LBA 1 +#define NX_GPT_NUM_BLOCKS 33 +#define NX_EMMC_BLOCKSIZE 512 + +typedef struct _emmc_part_t +{ + u32 lba_start; + u32 lba_end; + u64 attrs; + s8 name[37]; + link_t link; +} emmc_part_t; + +void nx_emmc_gpt_parse(link_t *gpt, sdmmc_storage_t *storage); +void nx_emmc_gpt_free(link_t *gpt); +emmc_part_t *nx_emmc_part_find(link_t *gpt, const char *name); +int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf); +int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf); + +#endif diff --git a/src/hwinit/pinmux.c b/src/hwinit/pinmux.c new file mode 100644 index 0000000..d0c03d8 --- /dev/null +++ b/src/hwinit/pinmux.c @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "pinmux.h" +#include "t210.h" + +void pinmux_config_uart(u32 idx) +{ + PINMUX_AUX(PINMUX_AUX_UARTX_RX(idx)) = 0; + PINMUX_AUX(PINMUX_AUX_UARTX_TX(idx)) = 0x48; + PINMUX_AUX(PINMUX_AUX_UARTX_RTS(idx)) = 0; + PINMUX_AUX(PINMUX_AUX_UARTX_CTS(idx)) = 0x44; +} + +void pinmux_config_i2c(u32 idx) +{ + PINMUX_AUX(PINMUX_AUX_X_I2C_SCL(idx)) = 0x40; + PINMUX_AUX(PINMUX_AUX_X_I2C_SDA(idx)) = 0x40; +} diff --git a/src/hwinit/pinmux.h b/src/hwinit/pinmux.h new file mode 100644 index 0000000..92df05d --- /dev/null +++ b/src/hwinit/pinmux.h @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _PINMUX_H_ +#define _PINMUX_H_ + +#include "types.h" + +/*! APB MISC registers. */ +#define APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL 0x8D4 +#define APB_MISC_GP_SDMMC3_CLK_LPBK_CONTROL 0x8D8 +#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98 +#define APB_MISC_GP_VGPIO_GPIO_MUX_SEL 0xB74 + +/*! Pinmux registers. */ +#define PINMUX_AUX_SDMMC1_CLK 0x00 +#define PINMUX_AUX_SDMMC1_CMD 0x04 +#define PINMUX_AUX_SDMMC1_DAT3 0x08 +#define PINMUX_AUX_SDMMC1_DAT2 0x0C +#define PINMUX_AUX_SDMMC1_DAT1 0x10 +#define PINMUX_AUX_SDMMC1_DAT0 0x14 +#define PINMUX_AUX_SDMMC3_CLK 0x1C +#define PINMUX_AUX_SDMMC3_CMD 0x20 +#define PINMUX_AUX_SDMMC3_DAT0 0x24 +#define PINMUX_AUX_SDMMC3_DAT1 0x28 +#define PINMUX_AUX_SDMMC3_DAT2 0x2C +#define PINMUX_AUX_SDMMC3_DAT3 0x30 +#define PINMUX_AUX_DMIC3_CLK 0xB4 +#define PINMUX_AUX_UART2_TX 0xF4 +#define PINMUX_AUX_UART3_TX 0x104 +#define PINMUX_AUX_NFC_EN 0x1D0 +#define PINMUX_AUX_NFC_INT 0x1D4 +#define PINMUX_AUX_LCD_BL_PWM 0x1FC +#define PINMUX_AUX_LCD_BL_EN 0x200 +#define PINMUX_AUX_LCD_RST 0x204 +#define PINMUX_AUX_GPIO_PE6 0x248 +#define PINMUX_AUX_GPIO_PH6 0x250 +#define PINMUX_AUX_GPIO_PZ1 0x280 +/*! 0:UART-A, 1:UART-B, 3:UART-C, 3:UART-D */ +#define PINMUX_AUX_UARTX_TX(x) (0xE4 + 0x10 * (x)) +#define PINMUX_AUX_UARTX_RX(x) (0xE8 + 0x10 * (x)) +#define PINMUX_AUX_UARTX_RTS(x) (0xEC + 0x10 * (x)) +#define PINMUX_AUX_UARTX_CTS(x) (0xF0 + 0x10 * (x)) +/*! 0:GEN1, 1:GEN2, 2:GEN3, 3:CAM, 4:PWR */ +#define PINMUX_AUX_X_I2C_SCL(x) (0xBC + 8 * (x)) +#define PINMUX_AUX_X_I2C_SDA(x) (0xC0 + 8 * (x)) + +#define PINMUX_FUNC_MASK (3 << 0) + +#define PINMUX_PULL_MASK (3 << 2) +#define PINMUX_PULL_NONE (0 << 2) +#define PINMUX_PULL_DOWN (1 << 2) +#define PINMUX_PULL_UP (2 << 2) + +#define PINMUX_TRISTATE (1 << 4) +#define PINMUX_PARKED (1 << 5) +#define PINMUX_INPUT_ENABLE (1 << 6) +#define PINMUX_LOCK (1 << 7) +#define PINMUX_LPDR (1 << 8) +#define PINMUX_HSM (1 << 9) + +#define PINMUX_IO_HV (1 << 10) +#define PINMUX_OPEN_DRAIN (1 << 11) +#define PINMUX_SCHMT (1 << 12) + +#define PINMUX_DRIVE_1X (0 << 13) +#define PINMUX_DRIVE_2X (1 << 13) +#define PINMUX_DRIVE_3X (2 << 13) +#define PINMUX_DRIVE_4X (3 << 13) + +void pinmux_config_uart(u32 idx); +void pinmux_config_i2c(u32 idx); + +#endif diff --git a/src/hwinit/pmc.h b/src/hwinit/pmc.h new file mode 100644 index 0000000..5c480bb --- /dev/null +++ b/src/hwinit/pmc.h @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (c) 2018 st4rk +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _PMC_H_ +#define _PMC_H_ + +/*! PMC registers. */ +#define APBDEV_PMC_PWRGATE_TOGGLE 0x30 +#define APBDEV_PMC_PWRGATE_STATUS 0x38 +#define APBDEV_PMC_NO_IOPOWER 0x44 +#define APBDEV_PMC_SCRATCH0 0x50 +#define APBDEV_PMC_SCRATCH1 0x54 +#define APBDEV_PMC_SCRATCH20 0xA0 +#define APBDEV_PMC_PWR_DET_VAL 0xE4 +#define APBDEV_PMC_DDR_PWR 0xE8 +#define APBDEV_PMC_CRYPTO_OP 0xF4 +#define APBDEV_PMC_OSC_EDPD_OVER 0x1A4 +#define APBDEV_PMC_IO_DPD_REQ 0x1B8 +#define APBDEV_PMC_IO_DPD2_REQ 0x1C0 +#define APBDEV_PMC_VDDP_SEL 0x1CC +#define APBDEV_PMC_TSC_MULT 0x2B4 +#define APBDEV_PMC_REG_SHORT 0x2CC +#define APBDEV_PMC_WEAK_BIAS 0x2C8 +#define APBDEV_PMC_SECURE_SCRATCH21 0x334 +#define APBDEV_PMC_CNTRL2 0x440 +#define APBDEV_PMC_IO_DPD4_REQ 0x464 +#define APBDEV_PMC_DDR_CNTRL 0x4E4 +#define APBDEV_PMC_SCRATCH188 0x810 +#define APBDEV_PMC_SCRATCH190 0x818 +#define APBDEV_PMC_SCRATCH200 0x840 +#define APBDEV_PMC_RST_STATUS_0 0x1B4 +#define APBDEV_PMC_SECURE_SCRATCH49_0 0x3A4 +#define APBDEV_PMC_SCRATCH49_0 0x244 + +#endif diff --git a/src/hwinit/pmc_t210_lp0.h b/src/hwinit/pmc_t210_lp0.h new file mode 100644 index 0000000..95dd033 --- /dev/null +++ b/src/hwinit/pmc_t210_lp0.h @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef _TEGRA210_PMC_H_ +#define _TEGRA210_PMC_H_ + +#include "types.h" + +struct tegra_pmc_regs { + u32 cntrl; + u32 sec_disable; + u32 pmc_swrst; + u32 wake_mask; + u32 wake_lvl; + u32 wake_status; + u32 sw_wake_status; + u32 dpd_pads_oride; + u32 dpd_sample; + u32 dpd_enable; + u32 pwrgate_timer_off; + u32 clamp_status; + u32 pwrgate_toggle; + u32 remove_clamping_cmd; + u32 pwrgate_status; + u32 pwrgood_timer; + u32 blink_timer; + u32 no_iopower; + u32 pwr_det; + u32 pwr_det_latch; + u32 scratch0; + u32 scratch1; + u32 scratch2; + u32 scratch3; + u32 scratch4; + u32 scratch5; + u32 scratch6; + u32 scratch7; + u32 scratch8; + u32 scratch9; + u32 scratch10; + u32 scratch11; + u32 scratch12; + u32 scratch13; + u32 scratch14; + u32 scratch15; + u32 scratch16; + u32 scratch17; + u32 scratch18; + u32 scratch19; + u32 odmdata; + u32 scratch21; + u32 scratch22; + u32 scratch23; + u32 secure_scratch0; + u32 secure_scratch1; + u32 secure_scratch2; + u32 secure_scratch3; + u32 secure_scratch4; + u32 secure_scratch5; + u32 cpupwrgood_timer; + u32 cpupwroff_timer; + u32 pg_mask; + u32 pg_mask_1; + u32 auto_wake_lvl; + u32 auto_wake_lvl_mask; + u32 wake_delay; + u32 pwr_det_val; + u32 ddr_pwr; + u32 usb_debounce_del; + u32 usb_a0; + u32 crypto_op; + u32 pllp_wb0_override; + u32 scratch24; + u32 scratch25; + u32 scratch26; + u32 scratch27; + u32 scratch28; + u32 scratch29; + u32 scratch30; + u32 scratch31; + u32 scratch32; + u32 scratch33; + u32 scratch34; + u32 scratch35; + u32 scratch36; + u32 scratch37; + u32 scratch38; + u32 scratch39; + u32 scratch40; + u32 scratch41; + u32 scratch42; + u32 bondout_mirror[3]; + u32 sys_33v_en; + u32 bondout_mirror_access; + u32 gate; + u32 wake2_mask; + u32 wake2_lvl; + u32 wake2_status; + u32 sw_wake2_status; + u32 auto_wake2_lvl_mask; + u32 pg_mask_2; + u32 pg_mask_ce1; + u32 pg_mask_ce2; + u32 pg_mask_ce3; + u32 pwrgate_timer_ce[7]; + u32 pcx_edpd_cntrl; + u32 osc_edpd_over; + u32 clk_out_cntrl; + u32 sata_pwrgt; + u32 sensor_ctrl; + u32 rst_status; + u32 io_dpd_req; + u32 io_dpd_status; + u32 io_dpd2_req; + u32 io_dpd2_status; + u32 sel_dpd_tim; + u32 vddp_sel; + u32 ddr_cfg; + u32 e_no_vttgen; + u8 _rsv0[4]; + u32 pllm_wb0_override_freq; + u32 test_pwrgate; + u32 pwrgate_timer_mult; + u32 dis_sel_dpd; + u32 utmip_uhsic_triggers; + u32 utmip_uhsic_saved_state; + u32 utmip_pad_cfg; + u32 utmip_term_pad_cfg; + u32 utmip_uhsic_sleep_cfg; + u32 utmip_uhsic_sleepwalk_cfg; + u32 utmip_sleepwalk_p[3]; + u32 uhsic_sleepwalk_p0; + u32 utmip_uhsic_status; + u32 utmip_uhsic_fake; + u32 bondout_mirror3[5 - 3]; + u32 secure_scratch6; + u32 secure_scratch7; + u32 scratch43; + u32 scratch44; + u32 scratch45; + u32 scratch46; + u32 scratch47; + u32 scratch48; + u32 scratch49; + u32 scratch50; + u32 scratch51; + u32 scratch52; + u32 scratch53; + u32 scratch54; + u32 scratch55; + u32 scratch0_eco; + u32 por_dpd_ctrl; + u32 scratch2_eco; + u32 utmip_uhsic_line_wakeup; + u32 utmip_bias_master_cntrl; + u32 utmip_master_config; + u32 td_pwrgate_inter_part_timer; + u32 utmip_uhsic2_triggers; + u32 utmip_uhsic2_saved_state; + u32 utmip_uhsic2_sleep_cfg; + u32 utmip_uhsic2_sleepwalk_cfg; + u32 uhsic2_sleepwalk_p1; + u32 utmip_uhsic2_status; + u32 utmip_uhsic2_fake; + u32 utmip_uhsic2_line_wakeup; + u32 utmip_master2_config; + u32 utmip_uhsic_rpd_cfg; + u32 pg_mask_ce0; + u32 pg_mask3[5 - 3]; + u32 pllm_wb0_override2; + u32 tsc_mult; + u32 cpu_vsense_override; + u32 glb_amap_cfg; + u32 sticky_bits; + u32 sec_disable2; + u32 weak_bias; + u32 reg_short; + u32 pg_mask_andor; + u8 _rsv1[0x2c]; + u32 secure_scratch8; /* offset 0x300 */ + u32 secure_scratch9; + u32 secure_scratch10; + u32 secure_scratch11; + u32 secure_scratch12; + u32 secure_scratch13; + u32 secure_scratch14; + u32 secure_scratch15; + u32 secure_scratch16; + u32 secure_scratch17; + u32 secure_scratch18; + u32 secure_scratch19; + u32 secure_scratch20; + u32 secure_scratch21; + u32 secure_scratch22; + u32 secure_scratch23; + u32 secure_scratch24; + u32 secure_scratch25; + u32 secure_scratch26; + u32 secure_scratch27; + u32 secure_scratch28; + u32 secure_scratch29; + u32 secure_scratch30; + u32 secure_scratch31; + u32 secure_scratch32; + u32 secure_scratch33; + u32 secure_scratch34; + u32 secure_scratch35; + u32 secure_scratch36; + u32 secure_scratch37; + u32 secure_scratch38; + u32 secure_scratch39; + u32 secure_scratch40; + u32 secure_scratch41; + u32 secure_scratch42; + u32 secure_scratch43; + u32 secure_scratch44; + u32 secure_scratch45; + u32 secure_scratch46; + u32 secure_scratch47; + u32 secure_scratch48; + u32 secure_scratch49; + u32 secure_scratch50; + u32 secure_scratch51; + u32 secure_scratch52; + u32 secure_scratch53; + u32 secure_scratch54; + u32 secure_scratch55; + u32 secure_scratch56; + u32 secure_scratch57; + u32 secure_scratch58; + u32 secure_scratch59; + u32 secure_scratch60; + u32 secure_scratch61; + u32 secure_scratch62; + u32 secure_scratch63; + u32 secure_scratch64; + u32 secure_scratch65; + u32 secure_scratch66; + u32 secure_scratch67; + u32 secure_scratch68; + u32 secure_scratch69; + u32 secure_scratch70; + u32 secure_scratch71; + u32 secure_scratch72; + u32 secure_scratch73; + u32 secure_scratch74; + u32 secure_scratch75; + u32 secure_scratch76; + u32 secure_scratch77; + u32 secure_scratch78; + u32 secure_scratch79; + u32 _rsv0x420[8]; + u32 cntrl2; /* 0x440 */ + u32 _rsv0x444[2]; + u32 event_counter; /* 0x44C */ + u32 fuse_control; + u32 scratch1_eco; + u32 _rsv0x458[1]; + u32 io_dpd3_req; /* 0x45C */ + u32 io_dpd3_status; + u32 io_dpd4_req; + u32 io_dpd4_status; + u32 _rsv0x46C[30]; + u32 ddr_cntrl; /* 0x4E4 */ + u32 _rsv0x4E8[70]; + u32 scratch56; /* 0x600 */ + u32 scratch57; + u32 scratch58; + u32 scratch59; + u32 scratch60; + u32 scratch61; + u32 scratch62; + u32 scratch63; + u32 scratch64; + u32 scratch65; + u32 scratch66; + u32 scratch67; + u32 scratch68; + u32 scratch69; + u32 scratch70; + u32 scratch71; + u32 scratch72; + u32 scratch73; + u32 scratch74; + u32 scratch75; + u32 scratch76; + u32 scratch77; + u32 scratch78; + u32 scratch79; + u32 scratch80; + u32 scratch81; + u32 scratch82; + u32 scratch83; + u32 scratch84; + u32 scratch85; + u32 scratch86; + u32 scratch87; + u32 scratch88; + u32 scratch89; + u32 scratch90; + u32 scratch91; + u32 scratch92; + u32 scratch93; + u32 scratch94; + u32 scratch95; + u32 scratch96; + u32 scratch97; + u32 scratch98; + u32 scratch99; + u32 scratch100; + u32 scratch101; + u32 scratch102; + u32 scratch103; + u32 scratch104; + u32 scratch105; + u32 scratch106; + u32 scratch107; + u32 scratch108; + u32 scratch109; + u32 scratch110; + u32 scratch111; + u32 scratch112; + u32 scratch113; + u32 scratch114; + u32 scratch115; + u32 scratch116; + u32 scratch117; + u32 scratch118; + u32 scratch119; + u32 scratch120; /* 0x700 */ + u32 scratch121; + u32 scratch122; + u32 scratch123; + u32 scratch124; + u32 scratch125; + u32 scratch126; + u32 scratch127; + u32 scratch128; + u32 scratch129; + u32 scratch130; + u32 scratch131; + u32 scratch132; + u32 scratch133; + u32 scratch134; + u32 scratch135; + u32 scratch136; + u32 scratch137; + u32 scratch138; + u32 scratch139; + u32 scratch140; + u32 scratch141; + u32 scratch142; + u32 scratch143; + u32 scratch144; + u32 scratch145; + u32 scratch146; + u32 scratch147; + u32 scratch148; + u32 scratch149; + u32 scratch150; + u32 scratch151; + u32 scratch152; + u32 scratch153; + u32 scratch154; + u32 scratch155; + u32 scratch156; + u32 scratch157; + u32 scratch158; + u32 scratch159; + u32 scratch160; + u32 scratch161; + u32 scratch162; + u32 scratch163; + u32 scratch164; + u32 scratch165; + u32 scratch166; + u32 scratch167; + u32 scratch168; + u32 scratch169; + u32 scratch170; + u32 scratch171; + u32 scratch172; + u32 scratch173; + u32 scratch174; + u32 scratch175; + u32 scratch176; + u32 scratch177; + u32 scratch178; + u32 scratch179; + u32 scratch180; + u32 scratch181; + u32 scratch182; + u32 scratch183; + u32 scratch184; + u32 scratch185; + u32 scratch186; + u32 scratch187; + u32 scratch188; + u32 scratch189; + u32 scratch190; + u32 scratch191; + u32 scratch192; + u32 scratch193; + u32 scratch194; + u32 scratch195; + u32 scratch196; + u32 scratch197; + u32 scratch198; + u32 scratch199; + u32 scratch200; + u32 scratch201; + u32 scratch202; + u32 scratch203; + u32 scratch204; + u32 scratch205; + u32 scratch206; + u32 scratch207; + u32 scratch208; + u32 scratch209; + u32 scratch210; + u32 scratch211; + u32 scratch212; + u32 scratch213; + u32 scratch214; + u32 scratch215; + u32 scratch216; + u32 scratch217; + u32 scratch218; + u32 scratch219; + u32 scratch220; + u32 scratch221; + u32 scratch222; + u32 scratch223; + u32 scratch224; + u32 scratch225; + u32 scratch226; + u32 scratch227; + u32 scratch228; + u32 scratch229; + u32 scratch230; + u32 scratch231; + u32 scratch232; + u32 scratch233; + u32 scratch234; + u32 scratch235; + u32 scratch236; + u32 scratch237; + u32 scratch238; + u32 scratch239; + u32 scratch240; + u32 scratch241; + u32 scratch242; + u32 scratch243; + u32 scratch244; + u32 scratch245; + u32 scratch246; + u32 scratch247; + u32 scratch248; + u32 scratch249; + u32 scratch250; + u32 scratch251; + u32 scratch252; + u32 scratch253; + u32 scratch254; + u32 scratch255; + u32 scratch256; + u32 scratch257; + u32 scratch258; + u32 scratch259; + u32 scratch260; + u32 scratch261; + u32 scratch262; + u32 scratch263; + u32 scratch264; + u32 scratch265; + u32 scratch266; + u32 scratch267; + u32 scratch268; + u32 scratch269; + u32 scratch270; + u32 scratch271; + u32 scratch272; + u32 scratch273; + u32 scratch274; + u32 scratch275; + u32 scratch276; + u32 scratch277; + u32 scratch278; + u32 scratch279; + u32 scratch280; + u32 scratch281; + u32 scratch282; + u32 scratch283; + u32 scratch284; + u32 scratch285; + u32 scratch286; + u32 scratch287; + u32 scratch288; + u32 scratch289; + u32 scratch290; + u32 scratch291; + u32 scratch292; + u32 scratch293; + u32 scratch294; + u32 scratch295; + u32 scratch296; + u32 scratch297; + u32 scratch298; + u32 scratch299; /* 0x9CC */ + u32 _rsv0x9D0[50]; + u32 secure_scratch80; /* 0xa98 */ + u32 secure_scratch81; + u32 secure_scratch82; + u32 secure_scratch83; + u32 secure_scratch84; + u32 secure_scratch85; + u32 secure_scratch86; + u32 secure_scratch87; + u32 secure_scratch88; + u32 secure_scratch89; + u32 secure_scratch90; + u32 secure_scratch91; + u32 secure_scratch92; + u32 secure_scratch93; + u32 secure_scratch94; + u32 secure_scratch95; + u32 secure_scratch96; + u32 secure_scratch97; + u32 secure_scratch98; + u32 secure_scratch99; + u32 secure_scratch100; + u32 secure_scratch101; + u32 secure_scratch102; + u32 secure_scratch103; + u32 secure_scratch104; + u32 secure_scratch105; + u32 secure_scratch106; + u32 secure_scratch107; + u32 secure_scratch108; + u32 secure_scratch109; + u32 secure_scratch110; + u32 secure_scratch111; + u32 secure_scratch112; + u32 secure_scratch113; + u32 secure_scratch114; + u32 secure_scratch115; + u32 secure_scratch116; + u32 secure_scratch117; + u32 secure_scratch118; + u32 secure_scratch119; +}; + +#endif /* _TEGRA210_PMC_H_ */ diff --git a/src/hwinit/sd.h b/src/hwinit/sd.h new file mode 100644 index 0000000..8fc328b --- /dev/null +++ b/src/hwinit/sd.h @@ -0,0 +1,109 @@ +/* +* include/linux/mmc/sd.h +* +* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. +* Copyright (C) 2018 CTCaer +* +* 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. +*/ + +#ifndef LINUX_MMC_SD_H +#define LINUX_MMC_SD_H + +/* SD commands type argument response */ +/* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */ + +/* class 10 */ +#define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + +/* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + +/* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SD_STATUS 13 /* adtc R1 */ +#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SET_CLR_CARD_DETECT 42 +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* OCR bit definitions */ +#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */ +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ +#define SD_OCR_XPC (1 << 28) /* SDXC power control */ +#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ +#define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */ + +/* +* SD_SWITCH argument format: +* +* [31] Check (0) or switch (1) +* [30:24] Reserved (0) +* [23:20] Function group 6 +* [19:16] Function group 5 +* [15:12] Function group 4 +* [11:8] Function group 3 +* [7:4] Function group 2 +* [3:0] Function group 1 +*/ + +/* +* SD_SEND_IF_COND argument format: +* +* [31:12] Reserved (0) +* [11:8] Host Voltage Supply Flags +* [7:0] Check Pattern (0xAA) +*/ + +/* +* SCR field definitions +*/ +#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ +#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ +#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */ +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) + +/* +* SD bus widths +*/ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* +* SD bus speeds +*/ +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 +#define HS400_BUS_SPEED 5 + +/* +* SD_SWITCH mode +*/ +#define SD_SWITCH_CHECK 0 +#define SD_SWITCH_SET 1 + +/* +* SD_SWITCH function groups +*/ +#define SD_SWITCH_GRP_ACCESS 0 + +/* +* SD_SWITCH access modes +*/ +#define SD_SWITCH_ACCESS_DEF 0 +#define SD_SWITCH_ACCESS_HS 1 + +#endif /* LINUX_MMC_SD_H */ diff --git a/src/hwinit/sdmmc.c b/src/hwinit/sdmmc.c new file mode 100644 index 0000000..30ec1fb --- /dev/null +++ b/src/hwinit/sdmmc.c @@ -0,0 +1,1129 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "sdmmc.h" +#include "mmc.h" +#include "sd.h" +#include "util.h" +#include "heap.h" + +/*#include "gfx.h" +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; +#define DPRINTF(...) gfx_printf(&gfx_con, __VA_ARGS__)*/ +#define DPRINTF(...) + +static inline u32 unstuff_bits(u32 *resp, u32 start, u32 size) +{ + const u32 mask = (size < 32 ? 1 << size : 0) - 1; + const u32 off = 3 - ((start) / 32); + const u32 shft = (start) & 31; + u32 res = resp[off] >> shft; + if (size + shft > 32) + res |= resp[off - 1] << ((32 - shft) % 32); + return res & mask; +} + +/* +* Common functions for SD and MMC. +*/ + +static int _sdmmc_storage_check_result(u32 res) +{ + //Error mask: + //R1_OUT_OF_RANGE, R1_ADDRESS_ERROR, R1_BLOCK_LEN_ERROR, + //R1_ERASE_SEQ_ERROR, R1_ERASE_PARAM, R1_WP_VIOLATION, + //R1_LOCK_UNLOCK_FAILED, R1_COM_CRC_ERROR, R1_ILLEGAL_COMMAND, + //R1_CARD_ECC_FAILED, R1_CC_ERROR, R1_ERROR, R1_CID_CSD_OVERWRITE, + //R1_WP_ERASE_SKIP, R1_ERASE_RESET, R1_SWITCH_ERROR + if (!(res & 0xFDF9A080)) + return 1; + //TODO: R1_SWITCH_ERROR we can skip for certain card types. + return 0; +} + +static int _sdmmc_storage_execute_cmd_type1_ex(sdmmc_storage_t *storage, u32 *resp, u32 cmd, u32 arg, u32 check_busy, u32 expected_state, u32 mask) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, cmd, arg, SDMMC_RSP_TYPE_1, check_busy); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 0; + + sdmmc_get_rsp(storage->sdmmc, resp, 4, SDMMC_RSP_TYPE_1); + if (mask) + *resp &= ~mask; + + if (_sdmmc_storage_check_result(*resp)) + if (expected_state == 0x10 || R1_CURRENT_STATE(*resp) == expected_state) + return 1; + return 0; +} + +static int _sdmmc_storage_execute_cmd_type1(sdmmc_storage_t *storage, u32 cmd, u32 arg, u32 check_busy, u32 expected_state) +{ + u32 tmp; + return _sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, cmd, arg, check_busy, expected_state, 0); +} + +static int _sdmmc_storage_go_idle_state(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmd; + sdmmc_init_cmd(&cmd, MMC_GO_IDLE_STATE, 0, SDMMC_RSP_TYPE_0, 0); + return sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0); +} + +static int _sdmmc_storage_get_cid(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmd; + sdmmc_init_cmd(&cmd, MMC_ALL_SEND_CID, 0, SDMMC_RSP_TYPE_2, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0)) + return 0; + sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); + return 1; +} + +static int _sdmmc_storage_select_card(sdmmc_storage_t *storage) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SELECT_CARD, storage->rca << 16, 1, 0x10); +} + +static int _sdmmc_storage_get_csd(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, MMC_SEND_CSD, storage->rca << 16, SDMMC_RSP_TYPE_2, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 0; + sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); + return 1; +} + +static int _sdmmc_storage_set_blocklen(sdmmc_storage_t *storage, u32 blocklen) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SET_BLOCKLEN, blocklen, 0, R1_STATE_TRAN); +} + +static int _sdmmc_storage_get_status(sdmmc_storage_t *storage, u32 *resp, u32 mask) +{ + return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, MMC_SEND_STATUS, storage->rca << 16, 0, R1_STATE_TRAN, mask); +} + +static int _sdmmc_storage_check_status(sdmmc_storage_t *storage) +{ + u32 tmp; + return _sdmmc_storage_get_status(storage, &tmp, 0); +} + +static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out, u32 sector, u32 num_sectors, void *buf, u32 is_write) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, is_write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK, sector, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.num_sectors = num_sectors; + reqbuf.blksize = 512; + reqbuf.is_write = is_write; + reqbuf.is_multi_block = 1; + reqbuf.is_auto_cmd12 = 1; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, blkcnt_out)) + { + u32 tmp = 0; + sdmmc_stop_transmission(storage->sdmmc, &tmp); + _sdmmc_storage_get_status(storage, &tmp, 0); + return 0; + } + return 1; +} + +int sdmmc_storage_end(sdmmc_storage_t *storage) +{ + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + sdmmc_end(storage->sdmmc); + return 1; +} + +static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write) +{ + u8 *bbuf = (u8 *)buf; + + while (num_sectors) + { + u32 blkcnt = 0; + //Retry 9 times on error. + u32 retries = 10; + do + { + if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write)) + goto out; + else + retries--; + + sleep(100000); + } while (retries); + return 0; + +out:; + DPRINTF("readwrite: %08X\n", blkcnt); + sector += blkcnt; + num_sectors -= blkcnt; + bbuf += 512 * blkcnt; + } + return 1; +} + +int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + return _sdmmc_storage_readwrite(storage, sector, num_sectors, buf, 0); +} + +int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + return _sdmmc_storage_readwrite(storage, sector, num_sectors, buf, 1); +} + +/* +* MMC specific functions. +*/ + +static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u32 power) +{ + sdmmc_cmd_t cmd; + + u32 arg = 0; + switch (power) + { + case SDMMC_POWER_1_8: + arg = 0x40000080; //Sector access, voltage. + break; + case SDMMC_POWER_3_3: + arg = 0x403F8000; //Sector access, voltage. + break; + default: + return 0; + } + + sdmmc_init_cmd(&cmd, MMC_SEND_OP_COND, arg, SDMMC_RSP_TYPE_3, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0)) + return 0; + + return sdmmc_get_rsp(storage->sdmmc, pout, 4, SDMMC_RSP_TYPE_3); +} + +static int _mmc_storage_get_op_cond(sdmmc_storage_t *storage, u32 power) +{ + u32 timeout = get_tmr() + 1500000; + + while (1) + { + u32 cond = 0; + if (!_mmc_storage_get_op_cond_inner(storage, &cond, power)) + break; + if (cond & MMC_CARD_BUSY) + { + if (cond & 0x40000000) + storage->has_sector_access = 1; + return 1; + } + if (get_tmr() > timeout) + break; + sleep(1000); + } + + return 0; +} + +static int _mmc_storage_set_relative_addr(sdmmc_storage_t *storage) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SET_RELATIVE_ADDR, storage->rca << 16, 0, 0x10); +} + +static void _mmc_storage_parse_cid(sdmmc_storage_t *storage) +{ + u32 *raw_cid = (u32 *)&(storage->raw_cid); + + switch (storage->csd.mmca_vsn) + { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + storage->cid.prod_name[6] = unstuff_bits(raw_cid, 48, 8); + storage->cid.manfid = unstuff_bits(raw_cid, 104, 24); + storage->cid.hwrev = unstuff_bits(raw_cid, 44, 4); + storage->cid.fwrev = unstuff_bits(raw_cid, 40, 4); + storage->cid.serial = unstuff_bits(raw_cid, 16, 24); + break; + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + storage->cid.manfid = unstuff_bits(raw_cid, 120, 8); + storage->cid.card_bga = unstuff_bits(raw_cid, 112, 2); + storage->cid.oemid = unstuff_bits(raw_cid, 104, 8); + storage->cid.prv = unstuff_bits(raw_cid, 48, 8); + storage->cid.serial = unstuff_bits(raw_cid, 16, 32); + break; + default: + break; + } + + storage->cid.prod_name[0] = unstuff_bits(raw_cid, 96, 8); + storage->cid.prod_name[1] = unstuff_bits(raw_cid, 88, 8); + storage->cid.prod_name[2] = unstuff_bits(raw_cid, 80, 8); + storage->cid.prod_name[3] = unstuff_bits(raw_cid, 72, 8); + storage->cid.prod_name[4] = unstuff_bits(raw_cid, 64, 8); + storage->cid.prod_name[5] = unstuff_bits(raw_cid, 56, 8); + + storage->cid.month = unstuff_bits(raw_cid, 12, 4); + storage->cid.year = unstuff_bits(raw_cid, 8, 4) + 1997; + if (storage->ext_csd.rev >= 5) + { + if (storage->cid.year < 2010) + storage->cid.year += 16; + } +} + +static void _mmc_storage_parse_csd(sdmmc_storage_t *storage) +{ + u32 *raw_csd = (u32 *)&(storage->raw_csd); + + storage->csd.mmca_vsn = unstuff_bits(raw_csd, 122, 4); + storage->csd.structure = unstuff_bits(raw_csd, 126, 2); + storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12); + storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4); + storage->csd.capacity = (1 + unstuff_bits(raw_csd, 62, 12)) << (unstuff_bits(raw_csd, 47, 3) + 2); +} + +static void _mmc_storage_parse_ext_csd(sdmmc_storage_t *storage, u8 *buf) +{ + storage->ext_csd.rev = buf[EXT_CSD_REV]; + storage->ext_csd.ext_struct = buf[EXT_CSD_STRUCTURE]; + storage->ext_csd.card_type = buf[EXT_CSD_CARD_TYPE]; + storage->ext_csd.dev_version = *(u16 *)&buf[EXT_CSD_DEVICE_VERSION]; + storage->ext_csd.boot_mult = buf[EXT_CSD_BOOT_MULT]; + storage->ext_csd.rpmb_mult = buf[EXT_CSD_RPMB_MULT]; + storage->ext_csd.sectors = *(u32 *)&buf[EXT_CSD_SEC_CNT]; + storage->ext_csd.bkops = buf[EXT_CSD_BKOPS_SUPPORT]; + storage->ext_csd.bkops_en = buf[EXT_CSD_BKOPS_EN]; + storage->ext_csd.bkops_status = buf[EXT_CSD_BKOPS_STATUS]; + + storage->sec_cnt = *(u32 *)&buf[EXT_CSD_SEC_CNT]; +} + +static int _mmc_storage_get_ext_csd(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, MMC_SEND_EXT_CSD, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 512; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + _mmc_storage_parse_ext_csd(storage, buf); + + return _sdmmc_storage_check_result(tmp); +} + +static int _mmc_storage_switch(sdmmc_storage_t *storage, u32 arg) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SWITCH, arg, 1, 0x10); +} + +static int _mmc_storage_switch_buswidth(sdmmc_storage_t *storage, u32 bus_width) +{ + if (bus_width == SDMMC_BUS_WIDTH_1) + return 1; + + u32 arg = 0; + switch (bus_width) + { + case SDMMC_BUS_WIDTH_4: + arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + break; + case SDMMC_BUS_WIDTH_8: + arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8); + break; + } + + if (_mmc_storage_switch(storage, arg)) + if (_sdmmc_storage_check_status(storage)) + { + sdmmc_set_bus_width(storage->sdmmc, bus_width); + return 1; + } + + return 0; +} + +static int _mmc_storage_enable_HS(sdmmc_storage_t *storage, int check) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS))) + return 0; + if (check && !_sdmmc_storage_check_status(storage)) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 2)) + return 0; + DPRINTF("[mmc] switched to HS\n"); + if (check || _sdmmc_storage_check_status(storage)) + return 1; + return 0; +} + +static int _mmc_storage_enable_HS200(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200))) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 3)) + return 0; + if (!sdmmc_config_tuning(storage->sdmmc, 3, MMC_SEND_TUNING_BLOCK_HS200)) + return 0; + DPRINTF("[mmc] switched to HS200\n"); + return _sdmmc_storage_check_status(storage); +} + +static int _mmc_storage_enable_HS400(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_enable_HS200(storage)) + return 0; + sdmmc_get_venclkctl(storage->sdmmc); + if (!_mmc_storage_enable_HS(storage, 0)) + return 0; + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8))) + return 0; + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400))) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 4)) + return 0; + DPRINTF("[mmc] switched to HS400\n"); + return _sdmmc_storage_check_status(storage); +} + +static int _mmc_storage_enable_highspeed(sdmmc_storage_t *storage, u32 card_type, u32 type) +{ + //TODO: this should be a config item. + //---v + if (!1 || sdmmc_get_voltage(storage->sdmmc) != SDMMC_POWER_1_8) + goto out; + + if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 && + card_type & EXT_CSD_CARD_TYPE_HS400_1_8V && + type == 4) + return _mmc_storage_enable_HS400(storage); + + if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 || + (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_4 + && card_type & EXT_CSD_CARD_TYPE_HS200_1_8V + && (type == 4 || type == 3))) + return _mmc_storage_enable_HS200(storage); + +out:; + if (card_type & EXT_CSD_CARD_TYPE_HS_52) + return _mmc_storage_enable_HS(storage, 1); + return 1; +} + +static int _mmc_storage_enable_bkops(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_SET_BITS, EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_LEVEL_2))) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) +{ + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + storage->rca = 2; //TODO: this could be a config item. + + if (!sdmmc_init(sdmmc, id, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, 0, 0)) + return 0; + DPRINTF("[mmc] after init\n"); + + sleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + DPRINTF("[mmc] went to idle state\n"); + + if (!_mmc_storage_get_op_cond(storage, SDMMC_POWER_1_8)) + return 0; + DPRINTF("[mmc] got op cond\n"); + + if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) + return 0; + DPRINTF("[mmc] got cid\n"); + + if (!_mmc_storage_set_relative_addr(storage)) + return 0; + DPRINTF("[mmc] set relative addr\n"); + + if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) + return 0; + DPRINTF("[mmc] got csd\n"); + _mmc_storage_parse_csd(storage); + + if (!sdmmc_setup_clock(storage->sdmmc, 1)) + return 0; + DPRINTF("[mmc] after setup clock\n"); + + if (!_sdmmc_storage_select_card(storage)) + return 0; + DPRINTF("[mmc] card selected\n"); + + if (!_sdmmc_storage_set_blocklen(storage, 512)) + return 0; + DPRINTF("[mmc] set blocklen to 512\n"); + + u32 *csd = (u32 *)storage->raw_csd; + //Check system specification version, only version 4.0 and later support below features. + if (unstuff_bits(csd, 122, 4) < CSD_SPEC_VER_4) + { + storage->sec_cnt = (1 + unstuff_bits(csd, 62, 12)) << (unstuff_bits(csd, 47, 3) + 2); + return 1; + } + + if (!_mmc_storage_switch_buswidth(storage, bus_width)) + return 0; + DPRINTF("[mmc] switched buswidth\n"); + + u8 *ext_csd = (u8 *)malloc(512); + if (!_mmc_storage_get_ext_csd(storage, ext_csd)) + { + free(ext_csd); + return 0; + } + free(ext_csd); + DPRINTF("[mmc] got ext_csd\n"); + _mmc_storage_parse_cid(storage); //This needs to be after csd and ext_csd + //gfx_hexdump(&gfx_con, 0, ext_csd, 512); + + /* When auto BKOPS is enabled the mmc device should be powered all the time until we disable this and check status. + Disable it for now until BKOPS disable added to power down sequence at sdmmc_storage_end(). + Additionally this works only when we put the device in idle mode which we don't after enabling it. */ + if (storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2) && 0) + { + _mmc_storage_enable_bkops(storage); + DPRINTF("[mmc] BKOPS enabled\n"); + } + else + DPRINTF("[mmc] BKOPS disabled\n"); + + if (!_mmc_storage_enable_highspeed(storage, storage->ext_csd.card_type, type)) + return 0; + DPRINTF("[mmc] switched to highspeed mode\n"); + + sdmmc_sd_clock_ctrl(storage->sdmmc, 1); + + return 1; +} + +int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_PART_CONFIG, partition))) + return 0; + if (!_sdmmc_storage_check_status(storage)) + return 0; + storage->partition = partition; + return 1; +} + +/* +* SD specific functions. +*/ + +static int _sd_storage_execute_app_cmd(sdmmc_storage_t *storage, u32 expected_state, u32 mask, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + u32 tmp; + if (!_sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, MMC_APP_CMD, storage->rca << 16, 0, expected_state, mask)) + return 0; + return sdmmc_execute_cmd(storage->sdmmc, cmd, req, blkcnt_out); +} + +static int _sd_storage_execute_app_cmd_type1(sdmmc_storage_t *storage, u32 *resp, u32 cmd, u32 arg, u32 check_busy, u32 expected_state) +{ + if (!_sdmmc_storage_execute_cmd_type1(storage, MMC_APP_CMD, storage->rca << 16, 0, R1_STATE_TRAN)) + return 0; + return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, cmd, arg, check_busy, expected_state, 0); +} + +static int _sd_storage_send_if_cond(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SEND_IF_COND, 0x1AA, SDMMC_RSP_TYPE_5, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 1; // The SD Card is version 1.X + + u32 resp = 0; + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_5)) + return 2; + + return (resp & 0xFF) == 0xAA ? 0 : 2; +} + +static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int is_version_1, int supports_low_voltage) +{ + sdmmc_cmd_t cmdbuf; + // Support for Current > 150mA + u32 arg = (~is_version_1 & 1) ? SD_OCR_XPC : 0; + // Support for handling block-addressed SDHC cards + arg |= (~is_version_1 & 1) ? SD_OCR_CCS : 0; + // Support for 1.8V + arg |= (supports_low_voltage & ~is_version_1 & 1) ? SD_OCR_S18R : 0; + // This is needed for most cards. Do not set bit7 even if 1.8V is supported. + arg |= SD_OCR_VDD_32_33; + sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0); + if (!_sd_storage_execute_app_cmd(storage, 0x10, is_version_1 ? 0x400000 : 0, &cmdbuf, 0, 0)) + return 0; + return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3); +} + +static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage) +{ + u32 timeout = get_tmr() + 1500000; + + while (1) + { + u32 cond = 0; + if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage)) + break; + if (cond & MMC_CARD_BUSY) + { + if (cond & SD_OCR_CCS) + storage->has_sector_access = 1; + + if (cond & SD_ROCR_S18A && supports_low_voltage) + { + //The low voltage regulator configuration is valid for SDMMC1 only. + if (storage->sdmmc->id == SDMMC_1 && + _sdmmc_storage_execute_cmd_type1(storage, SD_SWITCH_VOLTAGE, 0, 0, R1_STATE_READY)) + { + if (!sdmmc_enable_low_voltage(storage->sdmmc)) + return 0; + storage->is_low_voltage = 1; + + DPRINTF("-> switched to low voltage\n"); + } + } + + return 1; + } + if (get_tmr() > timeout) + break; + sleep(10000); // Needs to be at least 10ms for some SD Cards + } + + return 0; +} + +static int _sd_storage_get_rca(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SEND_RELATIVE_ADDR, 0, SDMMC_RSP_TYPE_4, 0); + + u32 timeout = get_tmr() + 1500000; + + while (1) + { + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + break; + + u32 resp = 0; + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_4)) + break; + + if (resp >> 16) + { + storage->rca = resp >> 16; + return 1; + } + + if (get_tmr() > timeout) + break; + sleep(1000); + } + + return 0; +} + +static void _sd_storage_parse_scr(sdmmc_storage_t *storage) +{ + // unstuff_bits can parse only 4 u32 + u32 resp[4]; + + resp[3] = *(u32 *)&storage->raw_scr[4]; + resp[2] = *(u32 *)&storage->raw_scr[0]; + + storage->scr.sda_vsn = unstuff_bits(resp, 56, 4); + storage->scr.bus_widths = unstuff_bits(resp, 48, 4); + if (storage->scr.sda_vsn == SCR_SPEC_VER_2) + /* Check if Physical Layer Spec v3.0 is supported */ + storage->scr.sda_spec3 = unstuff_bits(resp, 47, 1); + if (storage->scr.sda_spec3) + storage->scr.cmds = unstuff_bits(resp, 32, 2); +} + +int _sd_storage_get_scr(sdmmc_storage_t *storage, u8 *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_APP_SEND_SCR, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 8; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + //Prepare buffer for unstuff_bits + for (int i = 0; i < 8; i+=4) + { + storage->raw_scr[i + 3] = buf[i]; + storage->raw_scr[i + 2] = buf[i + 1]; + storage->raw_scr[i + 1] = buf[i + 2]; + storage->raw_scr[i] = buf[i + 3]; + } + _sd_storage_parse_scr(storage); + //gfx_hexdump(&gfx_con, 0, storage->raw_scr, 8); + + return _sdmmc_storage_check_result(tmp); +} + +int _sd_storage_switch_get(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SWITCH, 0xFFFFFF, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_storage_check_result(tmp); +} + +int _sd_storage_switch(sdmmc_storage_t *storage, void *buf, int flag, u32 arg) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SWITCH, arg | (flag << 31) | 0xFFFFF0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_storage_check_result(tmp); +} + +int _sd_storage_enable_highspeed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf) +{ + if (!_sd_storage_switch(storage, buf, 0, hs_type)) + return 0; + + u32 type_out = buf[16] & 0xF; + if (type_out != hs_type) + return 0; + + if ((((u16)buf[0] << 8) | buf[1]) < 0x320) + { + if (!_sd_storage_switch(storage, buf, 1, hs_type)) + return 0; + + if (type_out != (buf[16] & 0xF)) + return 0; + } + + return 1; +} + +int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf) +{ + if (sdmmc_get_bus_width(storage->sdmmc) != SDMMC_BUS_WIDTH_4) + return 0; + + if (!_sd_storage_switch_get(storage, buf)) + return 0; + + u32 hs_type = 0; + switch (type) + { + case 11: + if (buf[13] & 8) + { + type = 11; + hs_type = UHS_SDR104_BUS_SPEED; + break; + } + //Fall through. + case 10: + if (!(buf[13] & 4)) + return 0; + type = 10; + hs_type = UHS_SDR50_BUS_SPEED; + break; + default: + return 0; + break; + } + + if (!_sd_storage_enable_highspeed(storage, hs_type, buf)) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, type)) + return 0; + if (!sdmmc_config_tuning(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK)) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int _sd_storage_enable_highspeed_high_volt(sdmmc_storage_t *storage, u8 *buf) +{ + if (!_sd_storage_switch_get(storage, buf)) + return 0; + + if (!(buf[13] & 2)) + return 1; + + if (!_sd_storage_enable_highspeed(storage, 1, buf)) + return 0; + if (!_sdmmc_storage_check_status(storage)) + return 0; + return sdmmc_setup_clock(storage->sdmmc, 7); +} + +static void _sd_storage_parse_ssr(sdmmc_storage_t *storage) +{ + // unstuff_bits supports only 4 u32 so break into 2 x 16byte groups + u32 raw_ssr1[4]; + u32 raw_ssr2[4]; + + raw_ssr1[3] = *(u32 *)&storage->raw_ssr[12]; + raw_ssr1[2] = *(u32 *)&storage->raw_ssr[8]; + raw_ssr1[1] = *(u32 *)&storage->raw_ssr[4]; + raw_ssr1[0] = *(u32 *)&storage->raw_ssr[0]; + + raw_ssr2[3] = *(u32 *)&storage->raw_ssr[28]; + raw_ssr2[2] = *(u32 *)&storage->raw_ssr[24]; + raw_ssr2[1] = *(u32 *)&storage->raw_ssr[20]; + raw_ssr2[0] = *(u32 *)&storage->raw_ssr[16]; + + storage->ssr.bus_width = (unstuff_bits(raw_ssr1, 510 - 384, 2) & SD_BUS_WIDTH_4) ? 4 : 1; + switch(unstuff_bits(raw_ssr1, 440 - 384, 8)) + { + case 0: + storage->ssr.speed_class = 0; + break; + case 1: + storage->ssr.speed_class = 2; + break; + case 2: + storage->ssr.speed_class = 4; + break; + case 3: + storage->ssr.speed_class = 6; + break; + case 4: + storage->ssr.speed_class = 10; + break; + default: + storage->ssr.speed_class = unstuff_bits(raw_ssr1, 440 - 384, 8); + break; + } + storage->ssr.uhs_grade = unstuff_bits(raw_ssr1, 396 - 384, 4); + storage->ssr.video_class = unstuff_bits(raw_ssr1, 384 - 384, 8); + + storage->ssr.app_class = unstuff_bits(raw_ssr2, 336 - 256, 4); +} + +static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_APP_SD_STATUS, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!(storage->csd.cmdclass & CCC_APP_SPEC)) { + DPRINTF("[sd] ssr: Card lacks mandatory SD Status function\n"); + return 0; + } + + if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + //Prepare buffer for unstuff_bits + for (int i = 0; i < 64; i+=4) + { + storage->raw_ssr[i + 3] = buf[i]; + storage->raw_ssr[i + 2] = buf[i + 1]; + storage->raw_ssr[i + 1] = buf[i + 2]; + storage->raw_ssr[i] = buf[i + 3]; + } + _sd_storage_parse_ssr(storage); + //gfx_hexdump(&gfx_con, 0, storage->raw_ssr, 64); + + return _sdmmc_storage_check_result(tmp); +} + +static void _sd_storage_parse_cid(sdmmc_storage_t *storage) +{ + u32 *raw_cid = (u32 *)&(storage->raw_cid); + + storage->cid.manfid = unstuff_bits(raw_cid, 120, 8); + storage->cid.oemid = unstuff_bits(raw_cid, 104, 16); + storage->cid.prod_name[0] = unstuff_bits(raw_cid, 96, 8); + storage->cid.prod_name[1] = unstuff_bits(raw_cid, 88, 8); + storage->cid.prod_name[2] = unstuff_bits(raw_cid, 80, 8); + storage->cid.prod_name[3] = unstuff_bits(raw_cid, 72, 8); + storage->cid.prod_name[4] = unstuff_bits(raw_cid, 64, 8); + storage->cid.hwrev = unstuff_bits(raw_cid, 60, 4); + storage->cid.fwrev = unstuff_bits(raw_cid, 56, 4); + storage->cid.serial = unstuff_bits(raw_cid, 24, 32); + storage->cid.month = unstuff_bits(raw_cid, 8, 4); + storage->cid.year = unstuff_bits(raw_cid, 12, 8) + 2000; +} + +static void _sd_storage_parse_csd(sdmmc_storage_t *storage) +{ + u32 *raw_csd = (u32 *)&(storage->raw_csd); + + storage->csd.structure = unstuff_bits(raw_csd, 126, 2); + storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12); + storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4); + storage->csd.write_protect = unstuff_bits(raw_csd, 12, 2); + switch(storage->csd.structure) + { + case 0: + storage->csd.capacity = (1 + unstuff_bits(raw_csd, 62, 12)) << (unstuff_bits(raw_csd, 47, 3) + 2); + break; + case 1: + storage->csd.c_size = (1 + unstuff_bits(raw_csd, 48, 22)); + storage->csd.capacity = storage->csd.c_size << 10; + storage->csd.read_blkbits = 9; + break; + } +} + +int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) +{ + int is_version_1 = 0; + + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + + if (!sdmmc_init(sdmmc, id, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0)) + return 0; + DPRINTF("[sd] after init\n"); + + sleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + DPRINTF("[sd] went to idle state\n"); + + is_version_1 = _sd_storage_send_if_cond(storage); + if (is_version_1 == 2) + return 0; + DPRINTF("[sd] after send if cond\n"); + + if (!_sd_storage_get_op_cond(storage, is_version_1, bus_width == SDMMC_BUS_WIDTH_4 && type == 11)) + return 0; + DPRINTF("[sd] got op cond\n"); + + if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) + return 0; + DPRINTF("[sd] got cid\n"); + _sd_storage_parse_cid(storage); + + if (!_sd_storage_get_rca(storage)) + return 0; + DPRINTF("[sd] got rca (= %04X)\n", storage->rca); + + if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) + return 0; + DPRINTF("[sd] got csd\n"); + + //Parse CSD. + _sd_storage_parse_csd(storage); + switch (storage->csd.structure) + { + case 0: + storage->sec_cnt = storage->csd.capacity; + break; + case 1: + storage->sec_cnt = storage->csd.c_size << 10; + break; + default: + DPRINTF("[sd] Unknown CSD structure %d\n", storage->csd.structure); + break; + } + + if (!storage->is_low_voltage) + { + if (!sdmmc_setup_clock(storage->sdmmc, 6)) + return 0; + DPRINTF("[sd] after setup clock\n"); + } + + if (!_sdmmc_storage_select_card(storage)) + return 0; + DPRINTF("[sd] card selected\n"); + + if (!_sdmmc_storage_set_blocklen(storage, 512)) + return 0; + DPRINTF("[sd] set blocklen to 512\n"); + + u32 tmp = 0; + if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_CLR_CARD_DETECT, 0, 0, R1_STATE_TRAN)) + return 0; + DPRINTF("[sd] cleared card detect\n"); + + u8 *buf = (u8 *)malloc(512); + if (!_sd_storage_get_scr(storage, buf)) + return 0; + //gfx_hexdump(&gfx_con, 0, storage->raw_scr, 8); + DPRINTF("[sd] got scr\n"); + + // Check if card supports a wider bus and if it's not SD Version 1.X + if (bus_width == SDMMC_BUS_WIDTH_4 && (storage->scr.bus_widths & 4) && (storage->scr.sda_vsn & 0xF)) + { + if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN)) + { + free(buf); + return 0; + } + sdmmc_set_bus_width(storage->sdmmc, SDMMC_BUS_WIDTH_4); + DPRINTF("[sd] switched to wide bus width\n"); + } + else + DPRINTF("[sd] SD does not support wide bus width\n"); + + if (storage->is_low_voltage) + { + if (!_sd_storage_enable_highspeed_low_volt(storage, type, buf)) + { + free(buf); + return 0; + } + DPRINTF("[sd] enabled highspeed (low voltage)\n"); + } + else if (type != 6 && (storage->scr.sda_vsn & 0xF) != 0) + { + if (!_sd_storage_enable_highspeed_high_volt(storage, buf)) + { + free(buf); + return 0; + } + DPRINTF("[sd] enabled highspeed (high voltage)\n"); + } + + sdmmc_sd_clock_ctrl(sdmmc, 1); + + // Parse additional card info from sd status + if (_sd_storage_get_ssr(storage, buf)) + DPRINTF("[sd] got sd status\n"); + + free(buf); + return 1; +} + +/* +* Gamecard specific functions. +*/ + +int _gc_storage_custom_cmd(sdmmc_storage_t *storage, void *buf) +{ + u32 resp; + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, 60, 0, SDMMC_RSP_TYPE_1, 1); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 1; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + { + sdmmc_stop_transmission(storage->sdmmc, &resp); + return 0; + } + + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_1)) + return 0; + if (!_sdmmc_storage_check_result(resp)) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc) +{ + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + + if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, 14, 0)) + return 0; + DPRINTF("[gc] after init\n"); + + sleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!sdmmc_config_tuning(storage->sdmmc, 14, MMC_SEND_TUNING_BLOCK_HS200)) + return 0; + DPRINTF("[gc] after tuning\n"); + + sdmmc_sd_clock_ctrl(sdmmc, 1); + + return 1; +} diff --git a/src/hwinit/sdmmc.h b/src/hwinit/sdmmc.h new file mode 100644 index 0000000..33e7ffa --- /dev/null +++ b/src/hwinit/sdmmc.h @@ -0,0 +1,111 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _SDMMC_H_ +#define _SDMMC_H_ + +#include "types.h" +#include "sdmmc_driver.h" + +typedef struct _mmc_cid +{ + u32 manfid; + u8 prod_name[8]; + u8 card_bga; + u8 prv; + u32 serial; + u16 oemid; + u16 year; + u8 hwrev; + u8 fwrev; + u8 month; +} mmc_cid_t; + +typedef struct _mmc_csd +{ + u8 structure; + u8 mmca_vsn; + u16 cmdclass; + u32 c_size; + u32 r2w_factor; + u32 max_dtr; + u32 erase_size; /* In sectors */ + u32 read_blkbits; + u32 write_blkbits; + u32 capacity; + u8 write_protect; +} mmc_csd_t; + +typedef struct _mmc_ext_csd +{ + u8 rev; + u32 sectors; + int bkops; /* background support bit */ + int bkops_en; /* manual bkops enable bit */ + u8 ext_struct; /* 194 */ + u8 card_type; /* 196 */ + u8 bkops_status; /* 246 */ + u16 dev_version; + u8 boot_mult; + u8 rpmb_mult; +} mmc_ext_csd_t; + +typedef struct _sd_scr +{ + u8 sda_vsn; + u8 sda_spec3; + u8 bus_widths; + u8 cmds; +} sd_scr_t; + +typedef struct _sd_ssr { + u8 bus_width; + u8 speed_class; + u8 uhs_grade; + u8 video_class; + u8 app_class; +} sd_ssr_t; + +/*! SDMMC storage context. */ +typedef struct _sdmmc_storage_t +{ + sdmmc_t *sdmmc; + u32 rca; + int has_sector_access; + u32 sec_cnt; + int is_low_voltage; + u32 partition; + u8 raw_cid[0x10]; + u8 raw_csd[0x10]; + u8 raw_scr[8]; + u8 raw_ssr[0x40]; + mmc_cid_t cid; + mmc_csd_t csd; + mmc_ext_csd_t ext_csd; + sd_scr_t scr; + sd_ssr_t ssr; +} sdmmc_storage_t; + +int sdmmc_storage_end(sdmmc_storage_t *storage); +int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); +int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition); +int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); +int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc); + +#endif diff --git a/src/hwinit/sdmmc_driver.c b/src/hwinit/sdmmc_driver.c new file mode 100644 index 0000000..4d08473 --- /dev/null +++ b/src/hwinit/sdmmc_driver.c @@ -0,0 +1,1092 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include + +#include "sdmmc.h" +#include "util.h" +#include "clock.h" +#include "mmc.h" +#include "max7762x.h" +#include "t210.h" +#include "pmc.h" +#include "pinmux.h" +#include "gpio.h" + +/*#include "gfx.h" +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; +#define DPRINTF(...) gfx_printf(&gfx_con, __VA_ARGS__)*/ +#define DPRINTF(...) + +/*! SCMMC controller base addresses. */ +static const u32 _sdmmc_bases[4] = { + 0x700B0000, + 0x700B0200, + 0x700B0400, + 0x700B0600, +}; + +int sdmmc_get_voltage(sdmmc_t *sdmmc) +{ + u32 p = sdmmc->regs->pwrcon; + if (!(p & TEGRA_MMC_PWRCTL_SD_BUS_POWER)) + return SDMMC_POWER_OFF; + if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8) + return SDMMC_POWER_1_8; + if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3) + return SDMMC_POWER_3_3; + return -1; +} + +static int _sdmmc_set_voltage(sdmmc_t *sdmmc, u32 power) +{ + u8 pwr = 0; + + switch (power) + { + case SDMMC_POWER_OFF: + sdmmc->regs->pwrcon &= ~TEGRA_MMC_PWRCTL_SD_BUS_POWER; + break; + case SDMMC_POWER_1_8: + sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + break; + case SDMMC_POWER_3_3: + sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + break; + default: + return 0; + } + + if (power != SDMMC_POWER_OFF) + { + pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->regs->pwrcon = pwr; + } + + return 1; +} + +u32 sdmmc_get_bus_width(sdmmc_t *sdmmc) +{ + u32 h = sdmmc->regs->hostctl; + if (h & TEGRA_MMC_HOSTCTL_8BIT) + return SDMMC_BUS_WIDTH_8; + if (h & TEGRA_MMC_HOSTCTL_4BIT) + return SDMMC_BUS_WIDTH_4; + return SDMMC_BUS_WIDTH_1; +} + +void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width) +{ + if (bus_width == SDMMC_BUS_WIDTH_1) + sdmmc->regs->hostctl &= ~(TEGRA_MMC_HOSTCTL_4BIT | TEGRA_MMC_HOSTCTL_8BIT); + else if (bus_width == SDMMC_BUS_WIDTH_4) + { + sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_4BIT; + sdmmc->regs->hostctl &= ~TEGRA_MMC_HOSTCTL_8BIT; + } + else if (bus_width == SDMMC_BUS_WIDTH_8) + sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_8BIT; +} + +void sdmmc_get_venclkctl(sdmmc_t *sdmmc) +{ + sdmmc->venclkctl_tap = sdmmc->regs->venclkctl >> 16; + sdmmc->venclkctl_set = 1; +} + +static int _sdmmc_config_ven_ceata_clk(sdmmc_t *sdmmc, u32 id) +{ + u32 tap_val = 0; + + if (id == 4) + sdmmc->regs->venceatactl = (sdmmc->regs->venceatactl & 0xFFFFC0FF) | 0x2800; + sdmmc->regs->field_1C0 &= 0xFFFDFFFF; + if (id == 4) + { + if (!sdmmc->venclkctl_set) + return 0; + tap_val = sdmmc->venclkctl_tap; + } + else + { + static const u32 tap_values[] = { 4, 0, 3, 0 }; + tap_val = tap_values[sdmmc->id]; + } + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16); + + return 1; +} + +static int _sdmmc_get_clkcon(sdmmc_t *sdmmc) +{ + return sdmmc->regs->clkcon; +} + +static void _sdmmc_pad_config_fallback(sdmmc_t *sdmmc, u32 power) +{ + _sdmmc_get_clkcon(sdmmc); + if (sdmmc->id == SDMMC_4) + *(vu32 *)0x70000AB4 = ((*(vu32 *)0x70000AB4) & 0x3FFC) | 0x1040; + //TODO: load standard values for other controllers, can depend on power. +} + +static int _sdmmc_wait_type4(sdmmc_t *sdmmc) +{ + int res = 1, should_disable_sd_clock = 0; + + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + sdmmc->regs->field_1B0 |= 0x80000000; + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr() + 5000; + while (sdmmc->regs->field_1B0 & 0x80000000) + { + if (get_tmr() > timeout) + { + res = 0; + goto out; + } + } + + timeout = get_tmr() + 10000; + while (sdmmc->regs->field_1BC & 0x80000000) + { + if (get_tmr() > timeout) + { + res = 0; + goto out; + } + } + +out:; + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + return res; +} + +int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type) +{ + //Disable the SD clock if it was enabled, and reenable it later. + int should_enable_sd_clock = 0; + if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) + { + should_enable_sd_clock = 1; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + _sdmmc_config_ven_ceata_clk(sdmmc, type); + + switch (type) + { + case 0: + case 1: + case 5: + case 6: + sdmmc->regs->hostctl &= 0xFB; //Should this be 0xFFFB (~4) ? + sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + break; + case 2: + case 7: + sdmmc->regs->hostctl |= 4; + sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + break; + case 3: + case 11: + case 13: + case 14: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 4: + //Non standard + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | HS400_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 8: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR12_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 10: + //T210 Errata for SDR50, the host must be set to SDR104. + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + } + + _sdmmc_get_clkcon(sdmmc); + + u32 tmp; + u16 divisor; + clock_sdmmc_get_params(&tmp, &divisor, type); + clock_sdmmc_config_clock_source(&tmp, sdmmc->id, tmp); + sdmmc->divisor = (tmp + divisor - 1) / divisor; + + //if divisor != 1 && divisor << 31 -> error + + u16 div = divisor >> 1; + divisor = 0; + if (div > 0xFF) + divisor = div >> 8; + sdmmc->regs->clkcon = (sdmmc->regs->clkcon & 0x3F) | (div << 8) | (divisor << 6); + + //Enable the SD clock again. + if (should_enable_sd_clock) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + if (type == 4) + return _sdmmc_wait_type4(sdmmc); + return 1; +} + +static void _sdmmc_sd_clock_enable(sdmmc_t *sdmmc) +{ + if (!sdmmc->no_sd) + { + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + sdmmc->sd_clock_enabled = 1; +} + +static void _sdmmc_sd_clock_disable(sdmmc_t *sdmmc) +{ + sdmmc->sd_clock_enabled = 0; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd) +{ + sdmmc->no_sd = no_sd; + if (no_sd) + { + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + return; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + return; + } + if (sdmmc->sd_clock_enabled) + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +static int _sdmmc_cache_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) +{ + switch (type) + { + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_3: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (size < 4) + return 0; + rsp[0] = sdmmc->regs->rspreg0; + break; + case SDMMC_RSP_TYPE_2: + if (size < 0x10) + return 0; + // CRC is stripped, so shifting is needed. + u32 tempreg; + for (int i = 0; i < 4; i++) + { + switch(i) + { + case 0: + tempreg = sdmmc->regs->rspreg3; + break; + case 1: + tempreg = sdmmc->regs->rspreg2; + break; + case 2: + tempreg = sdmmc->regs->rspreg1; + break; + case 3: + tempreg = sdmmc->regs->rspreg0; + break; + } + rsp[i] = tempreg << 8; + + if (i != 0) + rsp[i - 1] |= (tempreg >> 24) & 0xFF; + } + break; + default: + return 0; + break; + } + + return 1; +} + +int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) +{ + if (!rsp || sdmmc->expected_rsp_type != type) + return 0; + + switch (type) + { + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_3: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (size < 4) + return 0; + rsp[0] = sdmmc->rsp[0]; + break; + case SDMMC_RSP_TYPE_2: + if (size < 0x10) + return 0; + rsp[0] = sdmmc->rsp[0]; + rsp[1] = sdmmc->rsp[1]; + rsp[2] = sdmmc->rsp[2]; + rsp[3] = sdmmc->rsp[3]; + break; + default: + return 0; + break; + } + + return 1; +} + +static void _sdmmc_reset(sdmmc_t *sdmmc) +{ + sdmmc->regs->swrst |= + TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE | TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE; + _sdmmc_get_clkcon(sdmmc); + u32 timeout = get_tmr() + 2000000; + while (sdmmc->regs->swrst << 29 >> 30 && get_tmr() < timeout) + ; +} + +static int _sdmmc_wait_prnsts_type0(sdmmc_t *sdmmc, u32 wait_dat) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr() + 2000000; + while(sdmmc->regs->prnsts & 1) //CMD inhibit. + if (get_tmr() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + + if (wait_dat) + { + timeout = get_tmr() + 2000000; + while (sdmmc->regs->prnsts & 2) //DAT inhibit. + if (get_tmr() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + } + + return 1; +} + +static int _sdmmc_wait_prnsts_type1(sdmmc_t *sdmmc) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr() + 2000000; + while (!(sdmmc->regs->prnsts & 0x100000)) //DAT0 line level. + if (get_tmr() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + + return 1; +} + +static int _sdmmc_setup_read_small_block(sdmmc_t *sdmmc) +{ + switch (sdmmc_get_bus_width(sdmmc)) + { + case SDMMC_BUS_WIDTH_1: + return 0; + break; + case SDMMC_BUS_WIDTH_4: + sdmmc->regs->blksize = 0x40; + break; + case SDMMC_BUS_WIDTH_8: + sdmmc->regs->blksize = 0x80; + break; + } + sdmmc->regs->blkcnt = 1; + sdmmc->regs->trnmod = TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + return 1; +} + +static int _sdmmc_parse_cmdbuf(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, int is_data_present) +{ + u16 cmdflags = 0; + + switch (cmd->rsp_type) + { + case SDMMC_RSP_TYPE_0: + break; + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (cmd->check_busy) + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY | + TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + else + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 | + TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + break; + case SDMMC_RSP_TYPE_2: + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + break; + case SDMMC_RSP_TYPE_3: + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + break; + default: + return 0; + break; + } + + if (is_data_present) + cmdflags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + sdmmc->regs->argument = cmd->arg; + sdmmc->regs->cmdreg = (cmd->cmd << 8) | cmdflags; + + return 1; +} + +static void _sdmmc_parse_cmd_48(sdmmc_t *sdmmc, u32 cmd) +{ + sdmmc_cmd_t cmdbuf; + cmdbuf.cmd = cmd; + cmdbuf.arg = 0; + cmdbuf.rsp_type = SDMMC_RSP_TYPE_1; + cmdbuf.check_busy = 0; + _sdmmc_parse_cmdbuf(sdmmc, &cmdbuf, 1); +} + +static int _sdmmc_config_tuning_once(sdmmc_t *sdmmc, u32 cmd) +{ + if (sdmmc->no_sd) + return 0; + if (!_sdmmc_wait_prnsts_type0(sdmmc, 1)) + return 0; + + _sdmmc_setup_read_small_block(sdmmc); + sdmmc->regs->norintstsen |= TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY; + sdmmc->regs->norintsts = sdmmc->regs->norintsts; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_parse_cmd_48(sdmmc, cmd); + _sdmmc_get_clkcon(sdmmc); + sleep(1); + _sdmmc_reset(sdmmc); + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr() + 5000; + while (get_tmr() < timeout) + { + if (sdmmc->regs->norintsts & 0x20) + { + sdmmc->regs->norintsts = 0x20; + sdmmc->regs->norintstsen &= 0xFFDF; + _sdmmc_get_clkcon(sdmmc); + sleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); + return 1; + } + } + _sdmmc_reset(sdmmc); + sdmmc->regs->norintstsen &= 0xFFDF; + _sdmmc_get_clkcon(sdmmc); + sleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); + return 0; +} + +int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd) +{ + u32 max = 0, flag = 0; + + sdmmc->regs->field_1C4 = 0; + switch (type) + { + case 3: + case 4: + case 11: + max = 0x80; + flag = 0x4000; + break; + case 10: + case 13: + case 14: + max = 0x100; + flag = 0x8000; + break; + default: + return 0; + } + + sdmmc->regs->field_1C0 = (sdmmc->regs->field_1C0 & 0xFFFF1FFF) | flag; + sdmmc->regs->field_1C0 = (sdmmc->regs->field_1C0 & 0xFFFFE03F) | 0x40; + sdmmc->regs->field_1C0 |= 0x20000; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_EXEC_TUNING; + + for (u32 i = 0; i < max; i++) + { + _sdmmc_config_tuning_once(sdmmc, cmd); + if (!(sdmmc->regs->hostctl2 & SDHCI_CTRL_EXEC_TUNING)) + break; + } + + if (sdmmc->regs->hostctl2 & SDHCI_CTRL_TUNED_CLK) + return 1; + return 0; +} + +static int _sdmmc_enable_internal_clock(sdmmc_t *sdmmc) +{ + //Enable internal clock and wait till it is stable. + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + u32 timeout = get_tmr() + 2000000; + while (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) + { + if (get_tmr() > timeout) + return 0; + } + + sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_PRESET_VAL_EN; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_CLKGEN_SELECT; + sdmmc->regs->hostctl2 |= SDHCI_HOST_VERSION_4_EN; + + if (!(sdmmc->regs->capareg & 0x10000000)) + return 0; + + sdmmc->regs->hostctl2 |= SDHCI_ADDRESSING_64BIT_EN; + sdmmc->regs->hostctl &= 0xE7; + sdmmc->regs->timeoutcon = (sdmmc->regs->timeoutcon & 0xF0) | 0xE; + + return 1; +} + +static int _sdmmc_autocal_config_offset(sdmmc_t *sdmmc, u32 power) +{ + u32 off_pd = 0; + u32 off_pu = 0; + + switch (sdmmc->id) + { + case SDMMC_2: + case SDMMC_4: + if (power != SDMMC_POWER_1_8) + return 0; + off_pd = 5; + off_pu = 5; + break; + case SDMMC_1: + case SDMMC_3: + if (power == SDMMC_POWER_1_8) + { + off_pd = 123; + off_pu = 123; + } + else if (power == SDMMC_POWER_3_3) + { + off_pd = 125; + off_pu = 0; + } + else + return 0; + break; + } + + sdmmc->regs->autocalcfg = (((sdmmc->regs->autocalcfg & 0xFFFF80FF) | (off_pd << 8)) >> 7 << 7) | off_pu; + return 1; +} + +static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power) +{ + int should_enable_sd_clock = 0; + if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) + { + should_enable_sd_clock = 1; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + if (!(sdmmc->regs->sdmemcmppadctl & 0x80000000)) + { + sdmmc->regs->sdmemcmppadctl |= 0x80000000; + _sdmmc_get_clkcon(sdmmc); + sleep(1); + } + + sdmmc->regs->autocalcfg |= 0xA0000000; + _sdmmc_get_clkcon(sdmmc); + sleep(1); + + u32 timeout = get_tmr() + 10000; + while (sdmmc->regs->autocalcfg & 0x80000000) + { + if (get_tmr() > timeout) + { + //In case autocalibration fails, we load suggested standard values. + _sdmmc_pad_config_fallback(sdmmc, power); + sdmmc->regs->autocalcfg &= 0xDFFFFFFF; + break; + } + } + + sdmmc->regs->sdmemcmppadctl &= 0x7FFFFFFF; + + if(should_enable_sd_clock) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +static void _sdmmc_enable_interrupts(sdmmc_t *sdmmc) +{ + sdmmc->regs->norintstsen |= 0xB; + sdmmc->regs->errintstsen |= 0x17F; + sdmmc->regs->norintsts = sdmmc->regs->norintsts; + sdmmc->regs->errintsts = sdmmc->regs->errintsts; +} + +static void _sdmmc_mask_interrupts(sdmmc_t *sdmmc) +{ + sdmmc->regs->errintstsen &= 0xFE80; + sdmmc->regs->norintstsen &= 0xFFF4; +} + +static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask) +{ + u16 norintsts = sdmmc->regs->norintsts; + u16 errintsts = sdmmc->regs->errintsts; + + DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts); + + if (pout) + *pout = norintsts; + + //Check for error interrupt. + if (norintsts & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + { + sdmmc->regs->errintsts = errintsts; + return SDMMC_MASKINT_ERROR; + } + else if (norintsts & mask) + { + sdmmc->regs->norintsts = norintsts & mask; + return SDMMC_MASKINT_MASKED; + } + + return SDMMC_MASKINT_NOERROR; +} + +static int _sdmmc_wait_request(sdmmc_t *sdmmc) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr() + 2000000; + while (1) + { + int res = _sdmmc_check_mask_interrupt(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); + if (res == SDMMC_MASKINT_MASKED) + break; + if (res != SDMMC_MASKINT_NOERROR || get_tmr() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + } + + return 1; +} + +static int _sdmmc_stop_transmission_inner(sdmmc_t *sdmmc, u32 *rsp) +{ + sdmmc_cmd_t cmd; + + if (!_sdmmc_wait_prnsts_type0(sdmmc, 0)) + return 0; + + _sdmmc_enable_interrupts(sdmmc); + cmd.cmd = MMC_STOP_TRANSMISSION; + cmd.arg = 0; + cmd.rsp_type = SDMMC_RSP_TYPE_1; + cmd.check_busy = 1; + _sdmmc_parse_cmdbuf(sdmmc, &cmd, 0); + int res = _sdmmc_wait_request(sdmmc); + _sdmmc_mask_interrupts(sdmmc); + + if (!res) + return 0; + + _sdmmc_cache_rsp(sdmmc, rsp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_wait_prnsts_type1(sdmmc); +} + +int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp) +{ + if (!sdmmc->sd_clock_enabled) + return 0; + + int should_disable_sd_clock = 0; + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + sleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + } + + int res = _sdmmc_stop_transmission_inner(sdmmc, rsp); + sleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + return res; +} + +static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req) +{ + if (!req->blksize || !req->num_sectors) + return 0; + + u32 blkcnt = req->num_sectors; + if (blkcnt >= 0xFFFF) + blkcnt = 0xFFFF; + u32 admaaddr = (u32)req->buf; + + //Check alignment. + if (admaaddr << 29) + return 0; + + sdmmc->regs->admaaddr = admaaddr; + sdmmc->regs->admaaddr_hi = 0; + + sdmmc->dma_addr_next = (admaaddr + 0x80000) & 0xFFF80000; + + sdmmc->regs->blksize = req->blksize | 0x7000; + sdmmc->regs->blkcnt = blkcnt; + + if (blkcnt_out) + *blkcnt_out = blkcnt; + + u32 trnmode = TEGRA_MMC_TRNMOD_DMA_ENABLE; + if (req->is_multi_block) + trnmode = TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE | + TEGRA_MMC_TRNMOD_DMA_ENABLE; + if (!req->is_write) + trnmode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + if (req->is_auto_cmd12) + trnmode = (trnmode & 0xFFF3) | TEGRA_MMC_TRNMOD_AUTO_CMD12; + + sdmmc->regs->trnmod = trnmode; + + return 1; +} + +static int _sdmmc_update_dma(sdmmc_t *sdmmc) +{ + u16 blkcnt = 0; + do + { + blkcnt = sdmmc->regs->blkcnt; + u32 timeout = get_tmr() + 1500000; + do + { + int res = 0; + while (1) + { + u16 intr = 0; + res = _sdmmc_check_mask_interrupt(sdmmc, &intr, + TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); + if (res < 0) + break; + if (intr & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) + return 1; //Transfer complete. + if (intr & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) + { + //Update DMA. + sdmmc->regs->admaaddr = sdmmc->dma_addr_next; + sdmmc->regs->admaaddr_hi = 0; + sdmmc->dma_addr_next += 0x80000; + } + } + if (res != SDMMC_MASKINT_NOERROR) + { + _sdmmc_reset(sdmmc); + return 0; + } + } while (get_tmr() < timeout); + } while (sdmmc->regs->blkcnt != blkcnt); + + _sdmmc_reset(sdmmc); + return 0; +} + +static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + int has_req_or_check_busy = req || cmd->check_busy; + if (!_sdmmc_wait_prnsts_type0(sdmmc, has_req_or_check_busy)) + return 0; + + u32 blkcnt = 0; + int is_data_present = 0; + if (req) + { + _sdmmc_config_dma(sdmmc, &blkcnt, req); + _sdmmc_enable_interrupts(sdmmc); + is_data_present = 1; + } + else + { + _sdmmc_enable_interrupts(sdmmc); + is_data_present = 0; + } + + _sdmmc_parse_cmdbuf(sdmmc, cmd, is_data_present); + + int res = _sdmmc_wait_request(sdmmc); + DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", res, + sdmmc->regs->rspreg0, sdmmc->regs->rspreg1, sdmmc->regs->rspreg2, sdmmc->regs->rspreg3); + if (res) + { + if (cmd->rsp_type) + { + sdmmc->expected_rsp_type = cmd->rsp_type; + _sdmmc_cache_rsp(sdmmc, sdmmc->rsp, 0x10, cmd->rsp_type); + } + if (req) + _sdmmc_update_dma(sdmmc); + } + + _sdmmc_mask_interrupts(sdmmc); + + if (res) + { + if (req) + { + if (blkcnt_out) + *blkcnt_out = blkcnt; + if (req->is_auto_cmd12) + sdmmc->rsp3 = sdmmc->regs->rspreg3; + } + + if (cmd->check_busy || req) + return _sdmmc_wait_prnsts_type1(sdmmc); + } + + return res; +} + +static int _sdmmc_config_sdmmc1() +{ + //Configure SD card detect. + PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = 0x49; //GPIO control, pull up. + APB_MISC(APB_MISC_GP_VGPIO_GPIO_MUX_SEL) = 0; + gpio_config(GPIO_PORT_Z, GPIO_PIN_1, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_Z, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); + sleep(100); + if(!!gpio_read(GPIO_PORT_Z, GPIO_PIN_1)) + return 0; + + /* + * Pinmux config: + * DRV_TYPE = DRIVE_2X + * E_SCHMT = ENABLE (for 1.8V), DISABLE (for 3.3V) + * E_INPUT = ENABLE + * TRISTATE = PASSTHROUGH + * APB_MISC_GP_SDMMCx_CLK_LPBK_CONTROL = SDMMCx_CLK_PAD_E_LPBK for CLK + */ + + //Configure SDMMC1 pinmux. + APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1; + PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = 0x2060; + PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = 0x2068; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) = 0x2068; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) = 0x2068; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = 0x2068; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = 0x2068; + + //Make sure the SDMMC1 controller is powered. + PMC(APBDEV_PMC_NO_IOPOWER) &= ~(1 << 12); + //Assume 3.3V SD card voltage. + PMC(APBDEV_PMC_PWR_DET_VAL) |= (1 << 12); + + //Set enable SD card power. + PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = 0x45; //GPIO control, pull down. + gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO); + gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH); + gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE); + + sleep(1000); + + //Enable SD card power. + max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000); + max77620_regulator_enable(REGULATOR_LDO2, 1); + + sleep(1000); + + //For good measure. + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x10000000; + + sleep(1000); + + return 1; +} + +int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd) +{ + if (id > SDMMC_4) + return 0; + + if (id == SDMMC_1) + if (!_sdmmc_config_sdmmc1()) + return 0; + + memset(sdmmc, 0, sizeof(sdmmc_t)); + + sdmmc->regs = (t210_sdmmc_t *)_sdmmc_bases[id]; + sdmmc->id = id; + sdmmc->clock_stopped = 1; + + if (clock_sdmmc_is_not_reset_and_enabled(id)) + { + _sdmmc_sd_clock_disable(sdmmc); + _sdmmc_get_clkcon(sdmmc); + } + + u32 clock; + u16 divisor; + clock_sdmmc_get_params(&clock, &divisor, type); + clock_sdmmc_enable(id, clock); + + sdmmc->clock_stopped = 0; + + //TODO: make this skip-able. + sdmmc->regs->field_1F0 |= 0x80000; + sdmmc->regs->field_1AC &= 0xFFFFFFFB; + static const u32 trim_values[] = { 2, 8, 3, 8 }; + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFF) | (trim_values[sdmmc->id] << 24); + sdmmc->regs->sdmemcmppadctl = (sdmmc->regs->sdmemcmppadctl & 0xF) | 7; + if (!_sdmmc_autocal_config_offset(sdmmc, power)) + return 0; + _sdmmc_autocal_execute(sdmmc, power); + if (_sdmmc_enable_internal_clock(sdmmc)) + { + sdmmc_set_bus_width(sdmmc, bus_width); + _sdmmc_set_voltage(sdmmc, power); + if (sdmmc_setup_clock(sdmmc, type)) + { + sdmmc_sd_clock_ctrl(sdmmc, no_sd); + _sdmmc_sd_clock_enable(sdmmc); + _sdmmc_get_clkcon(sdmmc); + return 1; + } + return 0; + } + return 0; +} + +void sdmmc_end(sdmmc_t *sdmmc) +{ + if (!sdmmc->clock_stopped) + { + _sdmmc_sd_clock_disable(sdmmc); + _sdmmc_set_voltage(sdmmc, SDMMC_POWER_OFF); + _sdmmc_get_clkcon(sdmmc); + clock_sdmmc_disable(sdmmc->id); + sdmmc->clock_stopped = 1; + } +} + +void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy) +{ + cmdbuf->cmd = cmd; + cmdbuf->arg = arg; + cmdbuf->rsp_type = rsp_type; + cmdbuf->check_busy = check_busy; +} + +int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + if (!sdmmc->sd_clock_enabled) + return 0; + + //Recalibrate periodically for SDMMC1. + if (sdmmc->id == SDMMC_1 && sdmmc->no_sd) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_voltage(sdmmc)); + + int should_disable_sd_clock = 0; + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + sleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + } + + int res = _sdmmc_execute_cmd_inner(sdmmc, cmd, req, blkcnt_out); + sleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + return res; +} + +int sdmmc_enable_low_voltage(sdmmc_t *sdmmc) +{ + if(sdmmc->id != SDMMC_1) + return 0; + + if (!sdmmc_setup_clock(sdmmc, 8)) + return 0; + + _sdmmc_get_clkcon(sdmmc); + + max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000); + PMC(APBDEV_PMC_PWR_DET_VAL) &= ~(1 << 12); + + _sdmmc_autocal_config_offset(sdmmc, SDMMC_POWER_1_8); + _sdmmc_autocal_execute(sdmmc, SDMMC_POWER_1_8); + _sdmmc_set_voltage(sdmmc, SDMMC_POWER_1_8); + _sdmmc_get_clkcon(sdmmc); + sleep(5000); + + if (sdmmc->regs->hostctl2 & SDHCI_CTRL_VDD_180) + { + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + sleep(1000u); + if ((sdmmc->regs->prnsts & 0xF00000) == 0xF00000) + return 1; + } + + return 0; +} diff --git a/src/hwinit/sdmmc_driver.h b/src/hwinit/sdmmc_driver.h new file mode 100644 index 0000000..2f232c1 --- /dev/null +++ b/src/hwinit/sdmmc_driver.h @@ -0,0 +1,126 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _SDMMC_DRIVER_H_ +#define _SDMMC_DRIVER_H_ + +#include "types.h" +#include "sdmmc_t210.h" + +/*! SDMMC controller IDs. */ +#define SDMMC_1 0 +#define SDMMC_2 1 +#define SDMMC_3 2 +#define SDMMC_4 3 + +/*! SDMMC power types. */ +#define SDMMC_POWER_OFF 0 +#define SDMMC_POWER_1_8 1 +#define SDMMC_POWER_3_3 2 + +/*! SDMMC bus widths. */ +#define SDMMC_BUS_WIDTH_1 0 +#define SDMMC_BUS_WIDTH_4 1 +#define SDMMC_BUS_WIDTH_8 2 + +/*! SDMMC response types. */ +#define SDMMC_RSP_TYPE_0 0 +#define SDMMC_RSP_TYPE_1 1 +#define SDMMC_RSP_TYPE_2 2 +#define SDMMC_RSP_TYPE_3 3 +#define SDMMC_RSP_TYPE_4 4 +#define SDMMC_RSP_TYPE_5 5 + +/*! SDMMC mask interrupt status. */ +#define SDMMC_MASKINT_MASKED 0 +#define SDMMC_MASKINT_NOERROR -1 +#define SDMMC_MASKINT_ERROR -2 + +/*! SDMMC host control 2 */ +#define SDHCI_CTRL_UHS_MASK 0xFFF8 +#define SDHCI_CTRL_VDD_330 0xFFF7 +#define SDHCI_CTRL_VDD_180 8 +#define SDHCI_CTRL_EXEC_TUNING 0x40 +#define SDHCI_CTRL_TUNED_CLK 0x80 +#define SDHCI_HOST_VERSION_4_EN 0x1000 +#define SDHCI_ADDRESSING_64BIT_EN 0x2000 +#define SDHCI_CTRL_PRESET_VAL_EN 0x8000 + +/*! SD bus speeds. */ +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 +#define HS400_BUS_SPEED 5 + +/*! Helper for SWITCH command argument. */ +#define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8)) + +/*! SDMMC controller context. */ +typedef struct _sdmmc_t +{ + t210_sdmmc_t *regs; + u32 id; + u32 divisor; + u32 clock_stopped; + int no_sd; + int sd_clock_enabled; + int venclkctl_set; + u32 venclkctl_tap; + u32 expected_rsp_type; + u32 dma_addr_next; + u32 rsp[4]; + u32 rsp3; +} sdmmc_t; + +/*! SDMMC command. */ +typedef struct _sdmmc_cmd_t +{ + u16 cmd; + u32 arg; + u32 rsp_type; + u32 check_busy; +} sdmmc_cmd_t; + +/*! SDMMC request. */ +typedef struct _sdmmc_req_t +{ + void *buf; + u32 blksize; + u32 num_sectors; + int is_write; + int is_multi_block; + int is_auto_cmd12; +} sdmmc_req_t; + +int sdmmc_get_voltage(sdmmc_t *sdmmc); +u32 sdmmc_get_bus_width(sdmmc_t *sdmmc); +void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width); +void sdmmc_get_venclkctl(sdmmc_t *sdmmc); +int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type); +void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd); +int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type); +int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd); +int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp); +int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd); +void sdmmc_end(sdmmc_t *sdmmc); +void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy); +int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out); +int sdmmc_enable_low_voltage(sdmmc_t *sdmmc); + +#endif diff --git a/src/hwinit/sdmmc_t210.h b/src/hwinit/sdmmc_t210.h new file mode 100644 index 0000000..80862c2 --- /dev/null +++ b/src/hwinit/sdmmc_t210.h @@ -0,0 +1,132 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _SDMMC_T210_H_ +#define _SDMMC_T210_H_ + +#include "types.h" + +#define TEGRA_MMC_PWRCTL_SD_BUS_POWER 0x1 +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 0xA +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 0xC +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 0xE +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_MASK 0xF1 + +#define TEGRA_MMC_HOSTCTL_1BIT 0x00 +#define TEGRA_MMC_HOSTCTL_4BIT 0x02 +#define TEGRA_MMC_HOSTCTL_8BIT 0x20 + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE 0x1 +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE 0x2 +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE 0x4 +#define TEGRA_MMC_CLKCON_CLKGEN_SELECT 0x20 + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL 0x1 +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE 0x2 +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE 0x4 + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE 0x1 +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE 0x2 +#define TEGRA_MMC_TRNMOD_AUTO_CMD12 0x4 +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE 0x0 +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ 0x10 +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT 0x20 + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK 0x8 +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK 0x10 +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER 0x20 + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK 0x3 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE 0x0 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 0x1 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 0x2 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY 0x3 + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE 0x1 +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE 0x2 +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT 0x8 +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT 0x8000 +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT 0x10000 + +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY 0x20 + +typedef struct _t210_sdmmc_t +{ + vu32 sysad; + vu16 blksize; + vu16 blkcnt; + vu32 argument; + vu16 trnmod; + vu16 cmdreg; + vu32 rspreg0; + vu32 rspreg1; + vu32 rspreg2; + vu32 rspreg3; + vu32 bdata; + vu32 prnsts; + vu8 hostctl; + vu8 pwrcon; + vu8 blkgap; + vu8 wakcon; + vu16 clkcon; + vu8 timeoutcon; + vu8 swrst; + vu16 norintsts; + vu16 errintsts; + vu16 norintstsen; + vu16 errintstsen; + vu16 norintsigen; + vu16 errintsigen; + vu16 acmd12errsts; + vu16 hostctl2; + vu32 capareg; + vu32 capareg_1; + vu32 maxcurr; + vu8 res3[4]; + vu16 setacmd12err; + vu16 setinterr; + vu8 admaerr; + vu8 res4[3]; + vu32 admaaddr; + vu32 admaaddr_hi; + vu8 res5[156]; + vu16 slotintstatus; + vu16 hcver; + vu32 venclkctl; + vu32 venspictl; + vu32 venspiintsts; + vu32 venceatactl; + vu32 venbootctl; + vu32 venbootacktout; + vu32 venbootdattout; + vu32 vendebouncecnt; + vu32 venmiscctl; + vu32 res6[34]; + vu32 field_1AC; + vu32 field_1B0; + vu8 res7[8]; + vu32 field_1BC; + vu32 field_1C0; + vu32 field_1C4; + vu8 field_1C8[24]; + vu32 sdmemcmppadctl; + vu32 autocalcfg; + vu32 autocalintval; + vu32 autocalsts; + vu32 field_1F0; +} t210_sdmmc_t; + +#endif diff --git a/src/hwinit/sdram.c b/src/hwinit/sdram.c new file mode 100644 index 0000000..c271391 --- /dev/null +++ b/src/hwinit/sdram.c @@ -0,0 +1,522 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "i2c.h" +#include "t210.h" +#include "mc.h" +#include "emc.h" +#include "pmc.h" +#include "util.h" +#include "max77620.h" +#include "sdram_param_t210.h" + +#define CONFIG_SDRAM_COMPRESS_CFG + +#ifdef CONFIG_SDRAM_COMPRESS_CFG +#include "lz.h" +#include "sdram_lz.inl" +#else +#include "sdram.inl" +#endif + +static u32 _get_sdram_id() +{ + return (FUSE_RESERVED_ODMX(4) & 0x38) >> 3; +} + +static void _sdram_config(const sdram_params_t *params) +{ + PMC(0x45C) = (((4 * params->emc_pmc_scratch1 >> 2) + 0x80000000) ^ 0xFFFF) & 0xC000FFFF; + sleep(params->pmc_io_dpd3_req_wait); + + u32 req = (4 * params->emc_pmc_scratch2 >> 2) + 0x80000000; + PMC(APBDEV_PMC_IO_DPD4_REQ) = (req >> 16 << 16) ^ 0x3FFF0000; + sleep(params->pmc_io_dpd4_req_wait); + PMC(APBDEV_PMC_IO_DPD4_REQ) = (req ^ 0xFFFF) & 0xC000FFFF; + sleep(params->pmc_io_dpd4_req_wait); + PMC(APBDEV_PMC_WEAK_BIAS) = 0; + sleep(1); + + CLOCK(0x98) = params->pllm_setup_control; + CLOCK(0x9C) = 0; + CLOCK(0x90) = (params->pllm_feedback_divider << 8) | params->pllm_input_divider | 0x40000000 | ((params->pllm_post_divider & 0xFFFF) << 20); + + u32 wait_end = TMR(0x10) + 300; + while (!(CLOCK(0x90) & 0x8000000)) + { + if (TMR(0x10) >= wait_end) + goto break_nosleep; + } + sleep(10); +break_nosleep: + + CLOCK(0x19C) = ((params->mc_emem_arb_misc0 >> 11) & 0x10000) | (params->emc_clock_source & 0xFFFEFFFF); + if (params->emc_clock_source_dll) + CLOCK(0x664) = params->emc_clock_source_dll; + if (params->clear_clock2_mc1) + CLOCK(0x44C) = 0x40000000; + CLOCK(0x328) = 0x2000001; + CLOCK(0x284) = 0x4000; + CLOCK(0x30C) = 0x2000001; + EMC(EMC_PMACRO_VTTGEN_CTRL_0) = params->emc_pmacro_vttgen_ctrl0; + EMC(EMC_PMACRO_VTTGEN_CTRL_1) = params->emc_pmacro_vttgen_ctrl1; + EMC(EMC_PMACRO_VTTGEN_CTRL_2) = params->emc_pmacro_vttgen_ctrl2; + EMC(EMC_TIMING_CONTROL) = 1; + sleep(1); + EMC(EMC_DBG) = (params->emc_dbg_write_mux << 1) | params->emc_dbg; + if (params->emc_bct_spare2) + *(vu32 *)params->emc_bct_spare2 = params->emc_bct_spare3; + EMC(EMC_FBIO_CFG7) = params->emc_fbio_cfg7; + EMC(EMC_CMD_MAPPING_CMD0_0) = params->emc_cmd_mapping_cmd0_0; + EMC(EMC_CMD_MAPPING_CMD0_1) = params->emc_cmd_mapping_cmd0_1; + EMC(EMC_CMD_MAPPING_CMD0_2) = params->emc_cmd_mapping_cmd0_2; + EMC(EMC_CMD_MAPPING_CMD1_0) = params->emc_cmd_mapping_cmd1_0; + EMC(EMC_CMD_MAPPING_CMD1_1) = params->emc_cmd_mapping_cmd1_1; + EMC(EMC_CMD_MAPPING_CMD1_2) = params->emc_cmd_mapping_cmd1_2; + EMC(EMC_CMD_MAPPING_CMD2_0) = params->emc_cmd_mapping_cmd2_0; + EMC(EMC_CMD_MAPPING_CMD2_1) = params->emc_cmd_mapping_cmd2_1; + EMC(EMC_CMD_MAPPING_CMD2_2) = params->emc_cmd_mapping_cmd2_2; + EMC(EMC_CMD_MAPPING_CMD3_0) = params->emc_cmd_mapping_cmd3_0; + EMC(EMC_CMD_MAPPING_CMD3_1) = params->emc_cmd_mapping_cmd3_1; + EMC(EMC_CMD_MAPPING_CMD3_2) = params->emc_cmd_mapping_cmd3_2; + EMC(EMC_CMD_MAPPING_BYTE) = params->emc_cmd_mapping_byte; + EMC(EMC_PMACRO_BRICK_MAPPING_0) = params->emc_pmacro_brick_mapping0; + EMC(EMC_PMACRO_BRICK_MAPPING_1) = params->emc_pmacro_brick_mapping1; + EMC(EMC_PMACRO_BRICK_MAPPING_2) = params->emc_pmacro_brick_mapping2; + EMC(EMC_PMACRO_BRICK_CTRL_RFU1) = (params->emc_pmacro_brick_ctrl_rfu1 & 0x1120112) | 0x1EED1EED; + EMC(EMC_CONFIG_SAMPLE_DELAY) = params->emc_config_sample_delay; + EMC(EMC_FBIO_CFG8) = params->emc_fbio_cfg8; + EMC(EMC_SWIZZLE_RANK0_BYTE0) = params->emc_swizzle_rank0_byte0; + EMC(EMC_SWIZZLE_RANK0_BYTE1) = params->emc_swizzle_rank0_byte1; + EMC(EMC_SWIZZLE_RANK0_BYTE2) = params->emc_swizzle_rank0_byte2; + EMC(EMC_SWIZZLE_RANK0_BYTE3) = params->emc_swizzle_rank0_byte3; + EMC(EMC_SWIZZLE_RANK1_BYTE0) = params->emc_swizzle_rank1_byte0; + EMC(EMC_SWIZZLE_RANK1_BYTE1) = params->emc_swizzle_rank1_byte1; + EMC(EMC_SWIZZLE_RANK1_BYTE2) = params->emc_swizzle_rank1_byte2; + EMC(EMC_SWIZZLE_RANK1_BYTE3) = params->emc_swizzle_rank1_byte3; + if (params->emc_bct_spare6) + *(vu32 *)params->emc_bct_spare6 = params->emc_bct_spare7; + EMC(EMC_XM2COMPPADCTRL) = params->emc_xm2_comp_pad_ctrl; + EMC(EMC_XM2COMPPADCTRL2) = params->emc_xm2_comp_pad_ctrl2; + EMC(EMC_XM2COMPPADCTRL3) = params->emc_xm2_comp_pad_ctrl3; + EMC(EMC_AUTO_CAL_CONFIG2) = params->emc_auto_cal_config2; + EMC(EMC_AUTO_CAL_CONFIG3) = params->emc_auto_cal_config3; + EMC(EMC_AUTO_CAL_CONFIG4) = params->emc_auto_cal_config4; + EMC(EMC_AUTO_CAL_CONFIG5) = params->emc_auto_cal_config5; + EMC(EMC_AUTO_CAL_CONFIG6) = params->emc_auto_cal_config6; + EMC(EMC_AUTO_CAL_CONFIG7) = params->emc_auto_cal_config7; + EMC(EMC_AUTO_CAL_CONFIG8) = params->emc_auto_cal_config8; + EMC(EMC_PMACRO_RX_TERM) = params->emc_pmacro_rx_term; + EMC(EMC_PMACRO_DQ_TX_DRV) = params->emc_pmacro_dq_tx_drive; + EMC(EMC_PMACRO_CA_TX_DRV) = params->emc_pmacro_ca_tx_drive; + EMC(EMC_PMACRO_CMD_TX_DRV) = params->emc_pmacro_cmd_tx_drive; + EMC(EMC_PMACRO_AUTOCAL_CFG_COMMON) = params->emc_pmacro_auto_cal_common; + EMC(EMC_AUTO_CAL_CHANNEL) = params->emc_auto_cal_channel; + EMC(EMC_PMACRO_ZCTRL) = params->emc_pmacro_zcrtl; + EMC(EMC_DLL_CFG_0) = params->emc_dll_cfg0; + EMC(EMC_DLL_CFG_1) = params->emc_dll_cfg1; + EMC(EMC_CFG_DIG_DLL_1) = params->emc_cfg_dig_dll_1; + EMC(EMC_DATA_BRLSHFT_0) = params->emc_data_brlshft0; + EMC(EMC_DATA_BRLSHFT_1) = params->emc_data_brlshft1; + EMC(EMC_DQS_BRLSHFT_0) = params->emc_dqs_brlshft0; + EMC(EMC_DQS_BRLSHFT_1) = params->emc_dqs_brlshft1; + EMC(EMC_CMD_BRLSHFT_0) = params->emc_cmd_brlshft0; + EMC(EMC_CMD_BRLSHFT_1) = params->emc_cmd_brlshft1; + EMC(EMC_CMD_BRLSHFT_2) = params->emc_cmd_brlshft2; + EMC(EMC_CMD_BRLSHFT_3) = params->emc_cmd_brlshft3; + EMC(EMC_QUSE_BRLSHFT_0) = params->emc_quse_brlshft0; + EMC(EMC_QUSE_BRLSHFT_1) = params->emc_quse_brlshft1; + EMC(EMC_QUSE_BRLSHFT_2) = params->emc_quse_brlshft2; + EMC(EMC_QUSE_BRLSHFT_3) = params->emc_quse_brlshft3; + EMC(EMC_PMACRO_BRICK_CTRL_RFU1) = (params->emc_pmacro_brick_ctrl_rfu1 & 0x1BF01BF) | 0x1E401E40; + EMC(EMC_PMACRO_PAD_CFG_CTRL) = params->emc_pmacro_pad_cfg_ctrl; + EMC(EMC_PMACRO_CMD_BRICK_CTRL_FDPD) = params->emc_pmacro_cmd_brick_ctrl_fdpd; + EMC(EMC_PMACRO_BRICK_CTRL_RFU2) = params->emc_pmacro_brick_ctrl_rfu2 & 0xFF7FFF7F; + EMC(EMC_PMACRO_DATA_BRICK_CTRL_FDPD) = params->emc_pmacro_data_brick_ctrl_fdpd; + EMC(EMC_PMACRO_BG_BIAS_CTRL_0) = params->emc_pmacro_bg_bias_ctrl0; + EMC(EMC_PMACRO_DATA_PAD_RX_CTRL) = params->emc_pmacro_data_pad_rx_ctrl; + EMC(EMC_PMACRO_CMD_PAD_RX_CTRL) = params->emc_pmacro_cmd_pad_rx_ctrl; + EMC(EMC_PMACRO_DATA_PAD_TX_CTRL) = params->emc_pmacro_data_pad_tx_ctrl; + EMC(EMC_PMACRO_DATA_RX_TERM_MODE) = params->emc_pmacro_data_rx_term_mode; + EMC(EMC_PMACRO_CMD_RX_TERM_MODE) = params->emc_pmacro_cmd_rx_term_mode; + EMC(EMC_PMACRO_CMD_PAD_TX_CTRL) = params->emc_pmacro_cmd_pad_tx_ctrl; + EMC(EMC_CFG_3) = params->emc_cfg3; + EMC(EMC_PMACRO_TX_PWRD_0) = params->emc_pmacro_tx_pwrd0; + EMC(EMC_PMACRO_TX_PWRD_1) = params->emc_pmacro_tx_pwrd1; + EMC(EMC_PMACRO_TX_PWRD_2) = params->emc_pmacro_tx_pwrd2; + EMC(EMC_PMACRO_TX_PWRD_3) = params->emc_pmacro_tx_pwrd3; + EMC(EMC_PMACRO_TX_PWRD_4) = params->emc_pmacro_tx_pwrd4; + EMC(EMC_PMACRO_TX_PWRD_5) = params->emc_pmacro_tx_pwrd5; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_0) = params->emc_pmacro_tx_sel_clk_src0; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_1) = params->emc_pmacro_tx_sel_clk_src1; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_2) = params->emc_pmacro_tx_sel_clk_src2; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_3) = params->emc_pmacro_tx_sel_clk_src3; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_4) = params->emc_pmacro_tx_sel_clk_src4; + EMC(EMC_PMACRO_TX_SEL_CLK_SRC_5) = params->emc_pmacro_tx_sel_clk_src5; + EMC(EMC_PMACRO_DDLL_BYPASS) = params->emc_pmacro_ddll_bypass; + EMC(EMC_PMACRO_DDLL_PWRD_0) = params->emc_pmacro_ddll_pwrd0; + EMC(EMC_PMACRO_DDLL_PWRD_1) = params->emc_pmacro_ddll_pwrd1; + EMC(EMC_PMACRO_DDLL_PWRD_2) = params->emc_pmacro_ddll_pwrd2; + EMC(EMC_PMACRO_CMD_CTRL_0) = params->emc_pmacro_cmd_ctrl0; + EMC(EMC_PMACRO_CMD_CTRL_1) = params->emc_pmacro_cmd_ctrl1; + EMC(EMC_PMACRO_CMD_CTRL_2) = params->emc_pmacro_cmd_ctrl2; + EMC(EMC_PMACRO_IB_VREF_DQ_0) = params->emc_pmacro_ib_vref_dq_0; + EMC(EMC_PMACRO_IB_VREF_DQ_1) = params->emc_pmacro_ib_vref_dq_1; + EMC(EMC_PMACRO_IB_VREF_DQS_0) = params->emc_pmacro_ib_vref_dqs_0; + EMC(EMC_PMACRO_IB_VREF_DQS_1) = params->emc_pmacro_ib_vref_dqs_1; + EMC(EMC_PMACRO_IB_RXRT) = params->emc_pmacro_ib_rxrt; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_0) = params->emc_pmacro_quse_ddll_rank0_0; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_1) = params->emc_pmacro_quse_ddll_rank0_1; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_2) = params->emc_pmacro_quse_ddll_rank0_2; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_3) = params->emc_pmacro_quse_ddll_rank0_3; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_4) = params->emc_pmacro_quse_ddll_rank0_4; + EMC(EMC_PMACRO_QUSE_DDLL_RANK0_5) = params->emc_pmacro_quse_ddll_rank0_5; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_0) = params->emc_pmacro_quse_ddll_rank1_0; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_1) = params->emc_pmacro_quse_ddll_rank1_1; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_2) = params->emc_pmacro_quse_ddll_rank1_2; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_3) = params->emc_pmacro_quse_ddll_rank1_3; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_4) = params->emc_pmacro_quse_ddll_rank1_4; + EMC(EMC_PMACRO_QUSE_DDLL_RANK1_5) = params->emc_pmacro_quse_ddll_rank1_5; + EMC(EMC_PMACRO_BRICK_CTRL_RFU1) = params->emc_pmacro_brick_ctrl_rfu1; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0) = params->emc_pmacro_ob_ddll_long_dq_rank0_0; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1) = params->emc_pmacro_ob_ddll_long_dq_rank0_1; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2) = params->emc_pmacro_ob_ddll_long_dq_rank0_2; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3) = params->emc_pmacro_ob_ddll_long_dq_rank0_3; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4) = params->emc_pmacro_ob_ddll_long_dq_rank0_4; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5) = params->emc_pmacro_ob_ddll_long_dq_rank0_5; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0) = params->emc_pmacro_ob_ddll_long_dq_rank1_0; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1) = params->emc_pmacro_ob_ddll_long_dq_rank1_1; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2) = params->emc_pmacro_ob_ddll_long_dq_rank1_2; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3) = params->emc_pmacro_ob_ddll_long_dq_rank1_3; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4) = params->emc_pmacro_ob_ddll_long_dq_rank1_4; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5) = params->emc_pmacro_ob_ddll_long_dq_rank1_5; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0) = params->emc_pmacro_ob_ddll_long_dqs_rank0_0; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1) = params->emc_pmacro_ob_ddll_long_dqs_rank0_1; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2) = params->emc_pmacro_ob_ddll_long_dqs_rank0_2; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3) = params->emc_pmacro_ob_ddll_long_dqs_rank0_3; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4) = params->emc_pmacro_ob_ddll_long_dqs_rank0_4; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5) = params->emc_pmacro_ob_ddll_long_dqs_rank0_5; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0) = params->emc_pmacro_ob_ddll_long_dqs_rank1_0; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1) = params->emc_pmacro_ob_ddll_long_dqs_rank1_1; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2) = params->emc_pmacro_ob_ddll_long_dqs_rank1_2; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3) = params->emc_pmacro_ob_ddll_long_dqs_rank1_3; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4) = params->emc_pmacro_ob_ddll_long_dqs_rank1_4; + EMC(EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5) = params->emc_pmacro_ob_ddll_long_dqs_rank1_5; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0) = params->emc_pmacro_ib_ddll_long_dqs_rank0_0; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1) = params->emc_pmacro_ib_ddll_long_dqs_rank0_1; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2) = params->emc_pmacro_ib_ddll_long_dqs_rank0_2; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3) = params->emc_pmacro_ib_ddll_long_dqs_rank0_3; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0) = params->emc_pmacro_ib_ddll_long_dqs_rank1_0; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1) = params->emc_pmacro_ib_ddll_long_dqs_rank1_1; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2) = params->emc_pmacro_ib_ddll_long_dqs_rank1_2; + EMC(EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3) = params->emc_pmacro_ib_ddll_long_dqs_rank1_3; + EMC(EMC_PMACRO_DDLL_LONG_CMD_0) = params->emc_pmacro_ddll_long_cmd_0; + EMC(EMC_PMACRO_DDLL_LONG_CMD_1) = params->emc_pmacro_ddll_long_cmd_1; + EMC(EMC_PMACRO_DDLL_LONG_CMD_2) = params->emc_pmacro_ddll_long_cmd_2; + EMC(EMC_PMACRO_DDLL_LONG_CMD_3) = params->emc_pmacro_ddll_long_cmd_3; + EMC(EMC_PMACRO_DDLL_LONG_CMD_4) = params->emc_pmacro_ddll_long_cmd_4; + EMC(EMC_PMACRO_DDLL_SHORT_CMD_0) = params->emc_pmacro_ddll_short_cmd_0; + EMC(EMC_PMACRO_DDLL_SHORT_CMD_1) = params->emc_pmacro_ddll_short_cmd_1; + EMC(EMC_PMACRO_DDLL_SHORT_CMD_2) = params->emc_pmacro_ddll_short_cmd_2; + EMC(EMC_PMACRO_COMMON_PAD_TX_CTRL) = (params->emc_pmacro_common_pad_tx_ctrl & 1) | 0xE; + if (params->emc_bct_spare4) + *(vu32 *)params->emc_bct_spare4 = params->emc_bct_spare5; + EMC(EMC_TIMING_CONTROL) = 1; + MC(MC_VIDEO_PROTECT_BOM) = params->mc_video_protect_bom; + MC(MC_VIDEO_PROTECT_BOM_ADR_HI) = params->mc_video_protect_bom_adr_hi; + MC(MC_VIDEO_PROTECT_SIZE_MB) = params->mc_video_protect_size_mb; + MC(MC_VIDEO_PROTECT_VPR_OVERRIDE) = params->mc_video_protect_vpr_override; + MC(MC_VIDEO_PROTECT_VPR_OVERRIDE1) = params->mc_video_protect_vpr_override1; + MC(MC_VIDEO_PROTECT_GPU_OVERRIDE_0) = params->mc_video_protect_gpu_override0; + MC(MC_VIDEO_PROTECT_GPU_OVERRIDE_1) = params->mc_video_protect_gpu_override1; + MC(MC_EMEM_ADR_CFG) = params->mc_emem_adr_cfg; + MC(MC_EMEM_ADR_CFG_DEV0) = params->mc_emem_adr_cfg_dev0; + MC(MC_EMEM_ADR_CFG_DEV1) = params->mc_emem_adr_cfg_dev1; + MC(MC_EMEM_ADR_CFG_CHANNEL_MASK) = params->mc_emem_adr_cfg_channel_mask; + MC(MC_EMEM_ADR_CFG_BANK_MASK_0) = params->mc_emem_adr_cfg_bank_mask0; + MC(MC_EMEM_ADR_CFG_BANK_MASK_1) = params->mc_emem_adr_cfg_bank_mask1; + MC(MC_EMEM_ADR_CFG_BANK_MASK_2) = params->mc_emem_adr_cfg_bank_mask2; + MC(MC_EMEM_CFG) = params->mc_emem_cfg; + MC(MC_SEC_CARVEOUT_BOM) = params->mc_sec_carveout_bom; + MC(MC_SEC_CARVEOUT_ADR_HI) = params->mc_sec_carveout_adr_hi; + MC(MC_SEC_CARVEOUT_SIZE_MB) = params->mc_sec_carveout_size_mb; + MC(MC_MTS_CARVEOUT_BOM) = params->mc_mts_carveout_bom; + MC(MC_MTS_CARVEOUT_ADR_HI) = params->mc_mts_carveout_adr_hi; + MC(MC_MTS_CARVEOUT_SIZE_MB) = params->mc_mts_carveout_size_mb; + MC(MC_EMEM_ARB_CFG) = params->mc_emem_arb_cfg; + MC(MC_EMEM_ARB_OUTSTANDING_REQ) = params->mc_emem_arb_outstanding_req; + MC(MC_EMEM_ARB_REFPB_HP_CTRL) = params->emc_emem_arb_refpb_hp_ctrl; + MC(MC_EMEM_ARB_REFPB_BANK_CTRL) = params->emc_emem_arb_refpb_bank_ctrl; + MC(MC_EMEM_ARB_TIMING_RCD) = params->mc_emem_arb_timing_rcd; + MC(MC_EMEM_ARB_TIMING_RP) = params->mc_emem_arb_timing_rp; + MC(MC_EMEM_ARB_TIMING_RC) = params->mc_emem_arb_timing_rc; + MC(MC_EMEM_ARB_TIMING_RAS) = params->mc_emem_arb_timing_ras; + MC(MC_EMEM_ARB_TIMING_FAW) = params->mc_emem_arb_timing_faw; + MC(MC_EMEM_ARB_TIMING_RRD) = params->mc_emem_arb_timing_rrd; + MC(MC_EMEM_ARB_TIMING_RAP2PRE) = params->mc_emem_arb_timing_rap2pre; + MC(MC_EMEM_ARB_TIMING_WAP2PRE) = params->mc_emem_arb_timing_wap2pre; + MC(MC_EMEM_ARB_TIMING_R2R) = params->mc_emem_arb_timing_r2r; + MC(MC_EMEM_ARB_TIMING_W2W) = params->mc_emem_arb_timing_w2w; + MC(MC_EMEM_ARB_TIMING_CCDMW) = params->mc_emem_arb_timing_ccdmw; + MC(MC_EMEM_ARB_TIMING_R2W) = params->mc_emem_arb_timing_r2w; + MC(MC_EMEM_ARB_TIMING_W2R) = params->mc_emem_arb_timing_w2r; + MC(MC_EMEM_ARB_TIMING_RFCPB) = params->mc_emem_arb_timing_rfcpb; + MC(MC_EMEM_ARB_DA_TURNS) = params->mc_emem_arb_da_turns; + MC(MC_EMEM_ARB_DA_COVERS) = params->mc_emem_arb_da_covers; + MC(MC_EMEM_ARB_MISC0) = params->mc_emem_arb_misc0; + MC(MC_EMEM_ARB_MISC1) = params->mc_emem_arb_misc1; + MC(MC_EMEM_ARB_MISC2) = params->mc_emem_arb_misc2; + MC(MC_EMEM_ARB_RING1_THROTTLE) = params->mc_emem_arb_ring1_throttle; + MC(MC_EMEM_ARB_OVERRIDE) = params->mc_emem_arb_override; + MC(MC_EMEM_ARB_OVERRIDE_1) = params->mc_emem_arb_override1; + MC(MC_EMEM_ARB_RSV) = params->mc_emem_arb_rsv; + MC(MC_DA_CONFIG0) = params->mc_da_cfg0; + MC(MC_TIMING_CONTROL) = 1; + MC(MC_CLKEN_OVERRIDE) = params->mc_clken_override; + MC(MC_STAT_CONTROL) = params->mc_stat_control; + EMC(EMC_ADR_CFG) = params->emc_adr_cfg; + EMC(EMC_CLKEN_OVERRIDE) = params->emc_clken_override; + EMC(EMC_PMACRO_AUTOCAL_CFG_0) = params->emc_pmacro_auto_cal_cfg0; + EMC(EMC_PMACRO_AUTOCAL_CFG_1) = params->emc_pmacro_auto_cal_cfg1; + EMC(EMC_PMACRO_AUTOCAL_CFG_2) = params->emc_pmacro_auto_cal_cfg2; + EMC(EMC_AUTO_CAL_VREF_SEL_0) = params->emc_auto_cal_vref_sel0; + EMC(EMC_AUTO_CAL_VREF_SEL_1) = params->emc_auto_cal_vref_sel1; + EMC(EMC_AUTO_CAL_INTERVAL) = params->emc_auto_cal_interval; + EMC(EMC_AUTO_CAL_CONFIG) = params->emc_auto_cal_config; + sleep(params->emc_auto_cal_wait); + if (params->emc_bct_spare8) + *(vu32 *)params->emc_bct_spare8 = params->emc_bct_spare9; + EMC(EMC_CFG_2) = params->emc_cfg2; + EMC(EMC_CFG_PIPE) = params->emc_cfg_pipe; + EMC(EMC_CFG_PIPE_1) = params->emc_cfg_pipe1; + EMC(EMC_CFG_PIPE_2) = params->emc_cfg_pipe2; + EMC(EMC_CMDQ) = params->emc_cmd_q; + EMC(EMC_MC2EMCQ) = params->emc_mc2emc_q; + EMC(EMC_MRS_WAIT_CNT) = params->emc_mrs_wait_cnt; + EMC(EMC_MRS_WAIT_CNT2) = params->emc_mrs_wait_cnt2; + EMC(EMC_FBIO_CFG5) = params->emc_fbio_cfg5; + EMC(EMC_RC) = params->emc_rc; + EMC(EMC_RFC) = params->emc_rfc; + EMC(EMC_RFCPB) = params->emc_rfc_pb; + EMC(EMC_REFCTRL2) = params->emc_ref_ctrl2; + EMC(EMC_RFC_SLR) = params->emc_rfc_slr; + EMC(EMC_RAS) = params->emc_ras; + EMC(EMC_RP) = params->emc_rp; + EMC(EMC_TPPD) = params->emc_tppd; + EMC(EMC_R2R) = params->emc_r2r; + EMC(EMC_W2W) = params->emc_w2w; + EMC(EMC_R2W) = params->emc_r2w; + EMC(EMC_W2R) = params->emc_w2r; + EMC(EMC_R2P) = params->emc_r2p; + EMC(EMC_W2P) = params->emc_w2p; + EMC(EMC_CCDMW) = params->emc_ccdmw; + EMC(EMC_RD_RCD) = params->emc_rd_rcd; + EMC(EMC_WR_RCD) = params->emc_wr_rcd; + EMC(EMC_RRD) = params->emc_rrd; + EMC(EMC_REXT) = params->emc_rext; + EMC(EMC_WEXT) = params->emc_wext; + EMC(EMC_WDV) = params->emc_wdv; + EMC(EMC_WDV_CHK) = params->emc_wdv_chk; + EMC(EMC_WSV) = params->emc_wsv; + EMC(EMC_WEV) = params->emc_wev; + EMC(EMC_WDV_MASK) = params->emc_wdv_mask; + EMC(EMC_WS_DURATION) = params->emc_ws_duration; + EMC(EMC_WE_DURATION) = params->emc_we_duration; + EMC(EMC_QUSE) = params->emc_quse; + EMC(EMC_QUSE_WIDTH) = params->emc_quse_width; + EMC(EMC_IBDLY) = params->emc_ibdly; + EMC(EMC_OBDLY) = params->emc_obdly; + EMC(EMC_EINPUT) = params->emc_einput; + EMC(EMC_EINPUT_DURATION) = params->emc_einput_duration; + EMC(EMC_PUTERM_EXTRA) = params->emc_puterm_extra; + EMC(EMC_PUTERM_WIDTH) = params->emc_puterm_width; + EMC(EMC_PMACRO_COMMON_PAD_TX_CTRL) = params->emc_pmacro_common_pad_tx_ctrl; + EMC(EMC_DBG) = params->emc_dbg; + EMC(EMC_QRST) = params->emc_qrst; + EMC(EMC_ISSUE_QRST) = 0; + EMC(EMC_QSAFE) = params->emc_qsafe; + EMC(EMC_RDV) = params->emc_rdv; + EMC(EMC_RDV_MASK) = params->emc_rdv_mask; + EMC(EMC_RDV_EARLY) = params->emc_rdv_early; + EMC(EMC_RDV_EARLY_MASK) = params->emc_rdv_early_mask; + EMC(EMC_QPOP) = params->emc_qpop; + EMC(EMC_REFRESH) = params->emc_refresh; + EMC(EMC_BURST_REFRESH_NUM) = params->emc_burst_refresh_num; + EMC(EMC_PRE_REFRESH_REQ_CNT) = params->emc_prerefresh_req_cnt; + EMC(EMC_PDEX2WR) = params->emc_pdex2wr; + EMC(EMC_PDEX2RD) = params->emc_pdex2rd; + EMC(EMC_PCHG2PDEN) = params->emc_pchg2pden; + EMC(EMC_ACT2PDEN) = params->emc_act2pden; + EMC(EMC_AR2PDEN) = params->emc_ar2pden; + EMC(EMC_RW2PDEN) = params->emc_rw2pden; + EMC(EMC_CKE2PDEN) = params->emc_cke2pden; + EMC(EMC_PDEX2CKE) = params->emc_pdex2che; + EMC(EMC_PDEX2MRR) = params->emc_pdex2mrr; + EMC(EMC_TXSR) = params->emc_txsr; + EMC(EMC_TXSRDLL) = params->emc_txsr_dll; + EMC(EMC_TCKE) = params->emc_tcke; + EMC(EMC_TCKESR) = params->emc_tckesr; + EMC(EMC_TPD) = params->emc_tpd; + EMC(EMC_TFAW) = params->emc_tfaw; + EMC(EMC_TRPAB) = params->emc_trpab; + EMC(EMC_TCLKSTABLE) = params->emc_tclkstable; + EMC(EMC_TCLKSTOP) = params->emc_tclkstop; + EMC(EMC_TREFBW) = params->emc_trefbw; + EMC(EMC_ODT_WRITE) = params->emc_odt_write; + EMC(EMC_CFG_DIG_DLL) = params->emc_cfg_dig_dll; + EMC(EMC_CFG_DIG_DLL_PERIOD) = params->emc_cfg_dig_dll_period; + EMC(EMC_FBIO_SPARE) = params->emc_fbio_spare & 0xFFFFFFFD; + EMC(EMC_CFG_RSV) = params->emc_cfg_rsv; + EMC(EMC_PMC_SCRATCH1) = params->emc_pmc_scratch1; + EMC(EMC_PMC_SCRATCH2) = params->emc_pmc_scratch2; + EMC(EMC_PMC_SCRATCH3) = params->emc_pmc_scratch3; + EMC(EMC_ACPD_CONTROL) = params->emc_acpd_control; + EMC(EMC_TXDSRVTTGEN) = params->emc_txdsrvttgen; + EMC(EMC_CFG) = (params->emc_cfg & 0xE) | 0x3C00000; + if (params->boot_rom_patch_control & 0x80000000) + { + *(vu32 *)(4 * (params->boot_rom_patch_control + 0x1C000000)) = params->boot_rom_patch_data; + MC(MC_TIMING_CONTROL) = 1; + } + PMC(0x45C) = ((4 * params->emc_pmc_scratch1 >> 2) + 0x40000000) & 0xCFFF0000; + sleep(params->pmc_io_dpd3_req_wait); + if (!params->emc_auto_cal_interval) + EMC(EMC_AUTO_CAL_CONFIG) = params->emc_auto_cal_config | 0x200; + EMC(EMC_PMACRO_BRICK_CTRL_RFU2) = params->emc_pmacro_brick_ctrl_rfu2; + if (params->emc_zcal_warm_cold_boot_enables & 1) + { + if (params->memory_type == 2) + EMC(EMC_ZCAL_WAIT_CNT) = 8 * params->emc_zcal_wait_cnt; + if (params->memory_type == 3) + { + EMC(EMC_ZCAL_WAIT_CNT) = params->emc_zcal_wait_cnt; + EMC(EMC_ZCAL_MRW_CMD) = params->emc_zcal_mrw_cmd; + } + } + EMC(EMC_TIMING_CONTROL) = 1; + sleep(params->emc_timing_control_wait); + PMC(0x4E4) &= 0xFFF8007F; + sleep(params->pmc_ddr_ctrl_wait); + if (params->memory_type == 2) + { + EMC(EMC_PIN) = (params->emc_pin_gpio_enable << 16) | (params->emc_pin_gpio << 12); + sleep(params->emc_pin_extra_wait + 200); + EMC(EMC_PIN) = ((params->emc_pin_gpio_enable << 16) | (params->emc_pin_gpio << 12)) + 256; + sleep(params->emc_pin_extra_wait + 500); + } + if (params->memory_type == 3) + { + EMC(EMC_PIN) = (params->emc_pin_gpio_enable << 16) | (params->emc_pin_gpio << 12); + sleep(params->emc_pin_extra_wait + 200); + EMC(EMC_PIN) = ((params->emc_pin_gpio_enable << 16) | (params->emc_pin_gpio << 12)) + 256; + sleep(params->emc_pin_extra_wait + 2000); + } + EMC(EMC_PIN) = ((params->emc_pin_gpio_enable << 16) | (params->emc_pin_gpio << 12)) + 0x101; + sleep(params->emc_pin_program_wait); + if (params->memory_type != 3) + EMC(EMC_NOP) = (params->emc_dev_select << 30) + 1; + if (params->memory_type == 1) + sleep(params->emc_pin_extra_wait + 200); + if (params->memory_type == 3) + { + if (params->emc_bct_spare10) + *(vu32 *)params->emc_bct_spare10 = params->emc_bct_spare11; + EMC(EMC_MRW2) = params->emc_mrw2; + EMC(EMC_MRW) = params->emc_mrw1; + EMC(EMC_MRW3) = params->emc_mrw3; + EMC(EMC_MRW4) = params->emc_mrw4; + EMC(EMC_MRW6) = params->emc_mrw6; + EMC(EMC_MRW14) = params->emc_mrw14; + EMC(EMC_MRW8) = params->emc_mrw8; + EMC(EMC_MRW12) = params->emc_mrw12; + EMC(EMC_MRW9) = params->emc_mrw9; + EMC(EMC_MRW13) = params->emc_mrw13; + if (params->emc_zcal_warm_cold_boot_enables & 1) + { + EMC(EMC_ZQ_CAL) = params->emc_zcal_init_dev0; + sleep(params->emc_zcal_init_wait); + EMC(EMC_ZQ_CAL) = params->emc_zcal_init_dev0 ^ 3; + if (!(params->emc_dev_select & 2)) + { + EMC(EMC_ZQ_CAL) = params->emc_zcal_init_dev1; + sleep(params->emc_zcal_init_wait); + EMC(EMC_ZQ_CAL) = params->emc_zcal_init_dev1 ^ 3; + } + } + } + PMC(0x1D0) = params->pmc_ddr_cfg; + if (params->memory_type - 1 <= 2) + { + EMC(EMC_ZCAL_INTERVAL) = params->emc_zcal_interval; + EMC(EMC_ZCAL_WAIT_CNT) = params->emc_zcal_wait_cnt; + EMC(EMC_ZCAL_MRW_CMD) = params->emc_zcal_mrw_cmd; + } + if (params->emc_bct_spare12) + *(vu32 *)params->emc_bct_spare12 = params->emc_bct_spare13; + EMC(EMC_TIMING_CONTROL) = 1; + if (params->emc_extra_refresh_num) + EMC(EMC_REF) = ((1 << params->emc_extra_refresh_num << 8) - 0xFD) | (params->emc_pin_gpio << 30); + EMC(EMC_REFCTRL) = params->emc_dev_select | 0x80000000; + EMC(EMC_DYN_SELF_REF_CONTROL) = params->emc_dyn_self_ref_control; + EMC(EMC_CFG_UPDATE) = params->emc_cfg_update; + EMC(EMC_CFG) = params->emc_cfg; + EMC(EMC_FDPD_CTRL_DQ) = params->emc_fdpd_ctrl_dq; + EMC(EMC_FDPD_CTRL_CMD) = params->emc_fdpd_ctrl_cmd; + EMC(EMC_SEL_DPD_CTRL) = params->emc_sel_dpd_ctrl; + EMC(EMC_FBIO_SPARE) = params->emc_fbio_spare | 2; + EMC(EMC_TIMING_CONTROL) = 1; + EMC(EMC_CFG_PIPE_CLK) = params->emc_cfg_pipe_clk; + EMC(EMC_FDPD_CTRL_CMD_NO_RAMP) = params->emc_fdpd_ctrl_cmd_no_ramp; + SYSREG(AHB_ARBITRATION_XBAR_CTRL) = (SYSREG(AHB_ARBITRATION_XBAR_CTRL) & 0xFFFEFFFF) | ((params->ahb_arbitration_xbar_ctrl_meminit_done & 0xFFFF) << 16); + MC(MC_VIDEO_PROTECT_REG_CTRL) = params->mc_video_protect_write_access; + MC(MC_SEC_CARVEOUT_REG_CTRL) = params->mc_sec_carveout_protect_write_access; + MC(MC_MTS_CARVEOUT_REG_CTRL) = params->mc_mts_carveout_reg_ctrl; + MC(MC_EMEM_CFG_ACCESS_CTRL) = 1; //Disable write access to a bunch of MC registers. +} + +const void *sdram_get_params() +{ + //TODO: sdram_id should be in [0, 7]. + +#ifdef CONFIG_SDRAM_COMPRESS_CFG + u8 *buf = (u8 *)0x40030000; + LZ_Uncompress(_dram_cfg_lz, buf, sizeof(_dram_cfg_lz)); + return (const void *)&buf[sizeof(sdram_params_t) * _get_sdram_id()]; +#else + return _dram_cfgs[_get_sdram_id()]; +#endif +} + +void sdram_init() +{ + //TODO: sdram_id should be in [0,4]. + const sdram_params_t *params = (const sdram_params_t *)sdram_get_params(); + + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_SD_CFG2, 0x05); + i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_SD1, 40); //40 = (1000 * 1100 - 600000) / 12500 -> 1.1V + + PMC(APBDEV_PMC_VDDP_SEL) = params->pmc_vddp_sel; + sleep(params->pmc_vddp_sel_wait); + PMC(APBDEV_PMC_DDR_PWR) = PMC(APBDEV_PMC_DDR_PWR); + PMC(APBDEV_PMC_NO_IOPOWER) = params->pmc_no_io_power; + PMC(APBDEV_PMC_REG_SHORT) = params->pmc_reg_short; + PMC(APBDEV_PMC_DDR_CNTRL) = params->pmc_ddr_ctrl; + + if (params->emc_bct_spare0) + *(vu32 *)params->emc_bct_spare0 = params->emc_bct_spare1; + + _sdram_config(params); +} diff --git a/src/hwinit/sdram.h b/src/hwinit/sdram.h new file mode 100644 index 0000000..df86631 --- /dev/null +++ b/src/hwinit/sdram.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _SDRAM_H_ +#define _SDRAM_H_ + +void sdram_init(); +const void *sdram_get_params(); +void sdram_lp0_save_params(const void *params); + +#endif diff --git a/src/hwinit/sdram.inl b/src/hwinit/sdram.inl new file mode 100644 index 0000000..2ad630e --- /dev/null +++ b/src/hwinit/sdram.inl @@ -0,0 +1,1152 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +static const u8 _dram_cfg_0[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_1[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_2[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_3[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_4[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x10, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0C, 0x00, + 0x02, 0x03, 0x0C, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x18, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_5[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, + 0x32, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x36, 0x00, + 0x2F, 0x00, 0x33, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x36, 0x00, + 0x2F, 0x00, 0x33, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x15, 0x00, 0x15, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, 0x16, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 _dram_cfg_6[1896] = { + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, 0x00, 0x04, 0xB4, 0x01, 0x70, + 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x1F, 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x77, 0x00, + 0xA6, 0xA6, 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC1, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x0D, 0xA0, 0x60, 0x91, + 0xBF, 0x3B, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x0C, 0x04, 0x05, 0x1B, 0x06, + 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, 0x08, 0x1D, 0x09, 0x0A, + 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, 0x02, 0x1B, 0x1C, 0x23, + 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, 0x0A, 0x0B, 0x1D, 0x0D, + 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, 0x24, 0x06, 0x07, 0x9A, + 0x12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x0D, 0x08, + 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, 0x00, 0x0B, 0x08, + 0x72, 0x72, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0D, 0x0C, + 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x04, 0x00, 0x01, 0x08, + 0x00, 0x00, 0x11, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0xCC, 0x00, + 0x0A, 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x00, 0x70, 0x00, 0x0C, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, + 0x01, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x2C, 0x00, 0x01, 0x37, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0xBE, 0x00, 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, + 0x32, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x36, 0x00, + 0x2F, 0x00, 0x33, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x36, 0x00, + 0x2F, 0x00, 0x33, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x15, 0x00, 0x15, 0x00, + 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x16, 0x00, 0x16, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x40, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22, 0x04, 0xFF, 0xFF, 0xAF, 0x4F, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xFF, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x32, 0x54, 0x76, 0x10, 0x47, 0x32, 0x65, + 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, 0x64, 0x32, + 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, 0x32, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x92, 0x24, 0x00, 0x49, 0x92, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2F, 0x41, 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, + 0xFF, 0xFF, 0xAF, 0x4F, 0xFF, 0xFF, 0xFF, 0x7F, 0x0B, 0xD7, 0x06, 0x40, + 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, 0x00, 0x00, 0x5C, 0x01, 0x00, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x11, 0x01, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x81, 0x10, 0x09, 0x28, 0x93, 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xDC, 0xDC, 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0A, 0x0A, 0x0A, 0x0A, 0x01, 0x00, 0x00, 0x00, 0x02, 0x03, 0x07, 0x00, + 0x02, 0x03, 0x07, 0x00, 0x00, 0x24, 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, + 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, 0x9C, 0x4B, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, 0x80, 0x20, 0x10, 0x0A, 0x00, + 0x28, 0x10, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x05, 0xA3, 0x72, 0x0F, 0x0F, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x16, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1E, 0x40, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x2C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u32 *_dram_cfgs[7] = { + (const u32 *)_dram_cfg_0, + (const u32 *)_dram_cfg_1, + (const u32 *)_dram_cfg_2, + (const u32 *)_dram_cfg_3, + (const u32 *)_dram_cfg_4, + (const u32 *)_dram_cfg_5, + (const u32 *)_dram_cfg_6 +}; diff --git a/src/hwinit/sdram_lp0.c b/src/hwinit/sdram_lp0.c new file mode 100644 index 0000000..1263287 --- /dev/null +++ b/src/hwinit/sdram_lp0.c @@ -0,0 +1,1102 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include "t210.h" +#include "pmc_t210_lp0.h" +#include "sdram_param_t210_lp0.h" + +/* + * This function reads SDRAM parameters from the common BCT format and + * writes them into PMC scratch registers (where the BootROM expects them + * on LP0 resume). + */ +void sdram_lp0_save_params(const void *params) +{ + struct sdram_params *sdram = (struct sdram_params *)params; + struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs *)PMC_BASE; + +#define pack(src, src_bits, dst, dst_bits) { \ + u32 mask = 0xffffffff >> (31 - ((1 ? src_bits) - (0 ? src_bits))); \ + dst &= ~(mask << (0 ? dst_bits)); \ + dst |= ((src >> (0 ? src_bits)) & mask) << (0 ? dst_bits); \ +} + +#define s(param, src_bits, pmcreg, dst_bits) \ + pack(sdram->param, src_bits, pmc->pmcreg, dst_bits) + +#define c(value, pmcreg, dst_bits) \ + pack(value, (1 ? dst_bits) - (0 ? dst_bits) : 0, pmc->pmcreg, dst_bits) + +/* 32 bits version of s macro */ +#define s32(param, pmcreg) pmc->pmcreg = sdram->param + +/* 32 bits version c macro */ +#define c32(value, pmcreg) pmc->pmcreg = value + + //TODO: pkg1.1 reads them from MC. + sdram->McGeneralizedCarveout1Bom = 0; + sdram->McGeneralizedCarveout1BomHi = 0; + sdram->McGeneralizedCarveout1Size128kb = 0; + sdram->McGeneralizedCarveout1Access0 = 0; + sdram->McGeneralizedCarveout1Access1 = 0; + sdram->McGeneralizedCarveout1Access2 = 0; + sdram->McGeneralizedCarveout1Access3 = 0; + sdram->McGeneralizedCarveout1Access4 = 0; + sdram->McGeneralizedCarveout1ForceInternalAccess0 = 0; + sdram->McGeneralizedCarveout1ForceInternalAccess1 = 0; + sdram->McGeneralizedCarveout1ForceInternalAccess2 = 0; + sdram->McGeneralizedCarveout1ForceInternalAccess3 = 0; + sdram->McGeneralizedCarveout1ForceInternalAccess4 = 0; + sdram->McGeneralizedCarveout1Cfg0 = 0; + sdram->McGeneralizedCarveout2Bom = 0x80020000; + sdram->McGeneralizedCarveout2BomHi = 0; + sdram->McGeneralizedCarveout2Size128kb = 2; + sdram->McGeneralizedCarveout2Access0 = 0; + sdram->McGeneralizedCarveout2Access1 = 0; + sdram->McGeneralizedCarveout2Access2 = 0x3000000; + sdram->McGeneralizedCarveout2Access3 = 0; + sdram->McGeneralizedCarveout2Access4 = 0x300; + sdram->McGeneralizedCarveout2ForceInternalAccess0 = 0; + sdram->McGeneralizedCarveout2ForceInternalAccess1 = 0; + sdram->McGeneralizedCarveout2ForceInternalAccess2 = 0; + sdram->McGeneralizedCarveout2ForceInternalAccess3 = 0; + sdram->McGeneralizedCarveout2ForceInternalAccess4 = 0; + sdram->McGeneralizedCarveout2Cfg0 = 0x440167E; + sdram->McGeneralizedCarveout3Bom = 0; + sdram->McGeneralizedCarveout3BomHi = 0; + sdram->McGeneralizedCarveout3Size128kb = 0; + sdram->McGeneralizedCarveout3Access0 = 0; + sdram->McGeneralizedCarveout3Access1 = 0; + sdram->McGeneralizedCarveout3Access2 = 0x3000000; + sdram->McGeneralizedCarveout3Access3 = 0; + sdram->McGeneralizedCarveout3Access4 = 0x300; + sdram->McGeneralizedCarveout3ForceInternalAccess0 = 0; + sdram->McGeneralizedCarveout3ForceInternalAccess1 = 0; + sdram->McGeneralizedCarveout3ForceInternalAccess2 = 0; + sdram->McGeneralizedCarveout3ForceInternalAccess3 = 0; + sdram->McGeneralizedCarveout3ForceInternalAccess4 = 0; + sdram->McGeneralizedCarveout3Cfg0 = 0x4401E7E; + sdram->McGeneralizedCarveout4Bom = 0; + sdram->McGeneralizedCarveout4BomHi = 0; + sdram->McGeneralizedCarveout4Size128kb = 0; + sdram->McGeneralizedCarveout4Access0 = 0; + sdram->McGeneralizedCarveout4Access1 = 0; + sdram->McGeneralizedCarveout4Access2 = 0; + sdram->McGeneralizedCarveout4Access3 = 0; + sdram->McGeneralizedCarveout4Access4 = 0; + sdram->McGeneralizedCarveout4ForceInternalAccess0 = 0; + sdram->McGeneralizedCarveout4ForceInternalAccess1 = 0; + sdram->McGeneralizedCarveout4ForceInternalAccess2 = 0; + sdram->McGeneralizedCarveout4ForceInternalAccess3 = 0; + sdram->McGeneralizedCarveout4ForceInternalAccess4 = 0; + sdram->McGeneralizedCarveout4Cfg0 = 0x8F; + sdram->McGeneralizedCarveout5Bom = 0; + sdram->McGeneralizedCarveout5BomHi = 0; + sdram->McGeneralizedCarveout5Size128kb = 0; + sdram->McGeneralizedCarveout5Access0 = 0; + sdram->McGeneralizedCarveout5Access1 = 0; + sdram->McGeneralizedCarveout5Access2 = 0; + sdram->McGeneralizedCarveout5Access3 = 0; + sdram->McGeneralizedCarveout5Access4 = 0; + sdram->McGeneralizedCarveout5ForceInternalAccess0 = 0; + sdram->McGeneralizedCarveout5ForceInternalAccess1 = 0; + sdram->McGeneralizedCarveout5ForceInternalAccess2 = 0; + sdram->McGeneralizedCarveout5ForceInternalAccess3 = 0; + sdram->McGeneralizedCarveout5ForceInternalAccess4 = 0; + sdram->McGeneralizedCarveout5Cfg0 = 0x8F; + + s(EmcClockSource, 7:0, scratch6, 15:8); + s(EmcClockSourceDll, 7:0, scratch6, 23:16); + s(EmcClockSource, 31:29, scratch6, 26:24); + s(EmcClockSourceDll, 31:29, scratch6, 29:27); + s(EmcClockSourceDll, 11:10, scratch6, 31:30); + s(ClkRstControllerPllmMisc2Override, 9:8, scratch7, 1:0); + s(ClkRstControllerPllmMisc2Override, 2:1, scratch7, 3:2); + s(EmcZqCalLpDdr4WarmBoot, 31:30, scratch7, 5:4); + s(EmcClockSource, 15:15, scratch7, 6:6); + s(EmcClockSource, 26:26, scratch7, 7:7); + s(EmcClockSource, 20:20, scratch7, 8:8); + s(EmcClockSource, 19:19, scratch7, 9:9); + s(ClkRstControllerPllmMisc2Override, 13:13, scratch7, 10:10); + s(ClkRstControllerPllmMisc2Override, 12:12, scratch7, 11:11); + s(ClkRstControllerPllmMisc2Override, 11:11, scratch7, 12:12); + s(ClkRstControllerPllmMisc2Override, 10:10, scratch7, 13:13); + s(ClkRstControllerPllmMisc2Override, 5:5, scratch7, 14:14); + s(ClkRstControllerPllmMisc2Override, 4:4, scratch7, 15:15); + s(ClkRstControllerPllmMisc2Override, 3:3, scratch7, 16:16); + s(ClkRstControllerPllmMisc2Override, 0:0, scratch7, 17:17); + s(EmcZqCalLpDdr4WarmBoot, 1:0, scratch7, 19:18); + s(EmcZqCalLpDdr4WarmBoot, 4:4, scratch7, 20:20); + s(EmcOdtWrite, 5:0, scratch7, 26:21); + s(EmcOdtWrite, 11:8, scratch7, 30:27); + s(EmcOdtWrite, 31:31, scratch7, 31:31); + s(EmcFdpdCtrlCmdNoRamp, 0:0, scratch13, 30:30); + s(EmcCfgPipeClk, 0:0, scratch13, 31:31); + s(McEmemArbMisc2, 0:0, scratch14, 30:30); + s(McDaCfg0, 0:0, scratch14, 31:31); + s(EmcQRst, 6:0, scratch15, 26:20); + s(EmcQRst, 20:16, scratch15, 31:27); + s(EmcPmacroCmdTxDrv, 5:0, scratch16, 25:20); + s(EmcPmacroCmdTxDrv, 13:8, scratch16, 31:26); + s(EmcPmacroAutocalCfg0, 2:0, scratch17, 22:20); + s(EmcPmacroAutocalCfg0, 10:8, scratch17, 25:23); + s(EmcPmacroAutocalCfg0, 18:16, scratch17, 28:26); + s(EmcPmacroAutocalCfg0, 26:24, scratch17, 31:29); + s(EmcPmacroAutocalCfg1, 2:0, scratch18, 22:20); + s(EmcPmacroAutocalCfg1, 10:8, scratch18, 25:23); + s(EmcPmacroAutocalCfg1, 18:16, scratch18, 28:26); + s(EmcPmacroAutocalCfg1, 26:24, scratch18, 31:29); + s(EmcPmacroAutocalCfg2, 2:0, scratch19, 22:20); + s(EmcPmacroAutocalCfg2, 10:8, scratch19, 25:23); + s(EmcPmacroAutocalCfg2, 18:16, scratch19, 28:26); + s(EmcPmacroAutocalCfg2, 26:24, scratch19, 31:29); + s32(EmcCfgRsv,scratch22); + s32(EmcAutoCalConfig, scratch23); + s32(EmcAutoCalVrefSel0, scratch24); + s32(EmcPmacroBrickCtrlRfu1, scratch25); + s32(EmcPmacroBrickCtrlRfu2, scratch26); + s32(EmcPmcScratch1, scratch27); + s32(EmcPmcScratch2, scratch28); + s32(EmcPmcScratch3, scratch29); + s32(McEmemArbDaTurns, scratch30); + s(EmcFbioSpare, 31:24, scratch58, 7:0); + s(EmcFbioSpare, 23:16, scratch58, 15:8); + s(EmcFbioSpare, 15:8, scratch58, 23:16); + s(EmcFbioSpare, 7:2, scratch58, 29:24); + s(EmcFbioSpare, 0:0, scratch58, 30:30); + s(EmcDllCfg0, 29:0, scratch59, 29:0); + s(EmcPmacroDdllBypass, 11:0, scratch60, 11:0); + s(EmcPmacroDdllBypass, 27:13, scratch60, 26:12); + s(EmcPmacroDdllBypass, 31:29, scratch60, 29:27); + s(McEmemArbMisc0, 14:0, scratch61, 14:0); + s(McEmemArbMisc0, 30:16, scratch61, 29:15); + s(EmcFdpdCtrlCmd, 16:0, scratch62, 16:0); + s(EmcFdpdCtrlCmd, 31:20, scratch62, 28:17); + s(EmcAutoCalConfig2, 27:0, scratch63, 27:0); + s(EmcBurstRefreshNum, 3:0, scratch63, 31:28); + s(EmcPmacroZctrl, 27:0, scratch64, 27:0); + s(EmcTppd, 3:0, scratch64, 31:28); + s(EmcCfgDigDll, 10:0, scratch65, 10:0); + s(EmcCfgDigDll, 25:12, scratch65, 24:11); + s(EmcCfgDigDll, 27:27, scratch65, 25:25); + s(EmcCfgDigDll, 31:30, scratch65, 27:26); + s(EmcR2r, 3:0, scratch65, 31:28); + s(EmcFdpdCtrlDq, 16:0, scratch66, 16:0); + s(EmcFdpdCtrlDq, 28:20, scratch66, 25:17); + s(EmcFdpdCtrlDq, 31:30, scratch66, 27:26); + s(EmcW2w, 3:0, scratch66, 31:28); + s(EmcPmacroTxPwrd4, 13:0, scratch67, 13:0); + s(EmcPmacroTxPwrd4, 29:16, scratch67, 27:14); + s(EmcPmacroCommonPadTxCtrl, 3:0, scratch67, 31:28); + s(EmcPmacroTxPwrd5, 13:0, scratch68, 13:0); + s(EmcPmacroTxPwrd5, 29:16, scratch68, 27:14); + s(EmcPmacroDdllPwrd0, 4:0, scratch69, 4:0); + s(EmcPmacroDdllPwrd0, 12:6, scratch69, 11:5); + s(EmcPmacroDdllPwrd0, 20:14, scratch69, 18:12); + s(EmcPmacroDdllPwrd0, 28:22, scratch69, 25:19); + s(EmcPmacroDdllPwrd0, 31:30, scratch69, 27:26); + s(EmcCfg, 4:4, scratch69, 31:31); + s(EmcPmacroDdllPwrd1, 4:0, scratch70, 4:0); + s(EmcPmacroDdllPwrd1, 12:6, scratch70, 11:5); + s(EmcPmacroDdllPwrd1, 20:14, scratch70, 18:12); + s(EmcPmacroDdllPwrd1, 28:22, scratch70, 25:19); + s(EmcPmacroDdllPwrd1, 31:30, scratch70, 27:26); + s(EmcCfg, 5:5, scratch70, 31:31); + s(EmcPmacroDdllPwrd2, 4:0, scratch71, 4:0); + s(EmcPmacroDdllPwrd2, 12:6, scratch71, 11:5); + s(EmcPmacroDdllPwrd2, 20:14, scratch71, 18:12); + s(EmcPmacroDdllPwrd2, 28:22, scratch71, 25:19); + s(EmcPmacroDdllPwrd2, 31:30, scratch71, 27:26); + s(EmcFbioCfg5, 23:20, scratch71, 31:28); + s(EmcPmacroIbVrefDq_0, 6:0, scratch72, 6:0); + s(EmcPmacroIbVrefDq_0, 14:8, scratch72, 13:7); + s(EmcPmacroIbVrefDq_0, 22:16, scratch72, 20:14); + s(EmcPmacroIbVrefDq_0, 30:24, scratch72, 27:21); + s(EmcFbioCfg5, 15:13, scratch72, 30:28); + s(EmcCfg, 6:6, scratch72, 31:31); + s(EmcPmacroIbVrefDq_1, 6:0, scratch73, 6:0); + s(EmcPmacroIbVrefDq_1, 14:8, scratch73, 13:7); + s(EmcPmacroIbVrefDq_1, 22:16, scratch73, 20:14); + s(EmcPmacroIbVrefDq_1, 30:24, scratch73, 27:21); + s(EmcCfg2, 5:3, scratch73, 30:28); + s(EmcCfg, 7:7, scratch73, 31:31); + s(EmcPmacroIbVrefDqs_0, 6:0, scratch74, 6:0); + s(EmcPmacroIbVrefDqs_0, 14:8, scratch74, 13:7); + s(EmcPmacroIbVrefDqs_0, 22:16, scratch74, 20:14); + s(EmcPmacroIbVrefDqs_0, 30:24, scratch74, 27:21); + s(EmcCfg, 17:16, scratch74, 29:28); + s(EmcFbioCfg5, 1:0, scratch74, 31:30); + s(EmcPmacroIbVrefDqs_1, 6:0, scratch75, 6:0); + s(EmcPmacroIbVrefDqs_1, 14:8, scratch75, 13:7); + s(EmcPmacroIbVrefDqs_1, 22:16, scratch75, 20:14); + s(EmcPmacroIbVrefDqs_1, 30:24, scratch75, 27:21); + s(EmcFbioCfg5, 3:2, scratch75, 29:28); + s(EmcCfg2, 27:26, scratch75, 31:30); + s(EmcPmacroDdllShortCmd_0, 6:0, scratch76, 6:0); + s(EmcPmacroDdllShortCmd_0, 14:8, scratch76, 13:7); + s(EmcPmacroDdllShortCmd_0, 22:16, scratch76, 20:14); + s(EmcPmacroDdllShortCmd_0, 30:24, scratch76, 27:21); + s(EmcPmacroCmdPadTxCtrl, 3:2, scratch76, 29:28); + s(EmcPmacroCmdPadTxCtrl, 7:6, scratch76, 31:30); + s(EmcPmacroDdllShortCmd_1, 6:0, scratch77, 6:0); + s(EmcPmacroDdllShortCmd_1, 14:8, scratch77, 13:7); + s(EmcPmacroDdllShortCmd_1, 22:16, scratch77, 20:14); + s(EmcPmacroDdllShortCmd_1, 30:24, scratch77, 27:21); + s(EmcPmacroCmdPadTxCtrl, 11:10, scratch77, 29:28); + s(EmcPmacroCmdPadTxCtrl, 15:14, scratch77, 31:30); + s(EmcAutoCalChannel, 5:0, scratch78, 5:0); + s(EmcAutoCalChannel, 11:8, scratch78, 9:6); + s(EmcAutoCalChannel, 27:16, scratch78, 21:10); + s(EmcAutoCalChannel, 31:29, scratch78, 24:22); + s(EmcConfigSampleDelay, 6:0, scratch78, 31:25); + s(EmcPmacroRxTerm, 5:0, scratch79, 5:0); + s(EmcPmacroRxTerm, 13:8, scratch79, 11:6); + s(EmcPmacroRxTerm, 21:16, scratch79, 17:12); + s(EmcPmacroRxTerm, 29:24, scratch79, 23:18); + s(EmcRc, 7:0, scratch79, 31:24); + s(EmcPmacroDqTxDrv, 5:0, scratch80, 5:0); + s(EmcPmacroDqTxDrv, 13:8, scratch80, 11:6); + s(EmcPmacroDqTxDrv, 21:16, scratch80, 17:12); + s(EmcPmacroDqTxDrv, 29:24, scratch80, 23:18); + s(EmcSelDpdCtrl, 5:2, scratch80, 27:24); + s(EmcSelDpdCtrl, 8:8, scratch80, 28:28); + s(EmcSelDpdCtrl, 18:16, scratch80, 31:29); + s(EmcPmacroCaTxDrv, 5:0, scratch81, 5:0); + s(EmcPmacroCaTxDrv, 13:8, scratch81, 11:6); + s(EmcPmacroCaTxDrv, 21:16, scratch81, 17:12); + s(EmcPmacroCaTxDrv, 29:24, scratch81, 23:18); + s(EmcObdly, 5:0, scratch81, 29:24); + s(EmcObdly, 29:28, scratch81, 31:30); + s(EmcZcalInterval, 23:10, scratch82, 13:0); + s(EmcZcalInterval, 9:0, scratch82, 23:14); + s(EmcPmacroCmdRxTermMode, 1:0, scratch82, 25:24); + s(EmcPmacroCmdRxTermMode, 5:4, scratch82, 27:26); + s(EmcPmacroCmdRxTermMode, 9:8, scratch82, 29:28); + s(EmcPmacroCmdRxTermMode, 13:12, scratch82, 31:30); + s(EmcDataBrlshft0, 23:0, scratch83, 23:0); + s(EmcPmacroDataRxTermMode, 1:0, scratch83, 25:24); + s(EmcPmacroDataRxTermMode, 5:4, scratch83, 27:26); + s(EmcPmacroDataRxTermMode, 9:8, scratch83, 29:28); + s(EmcPmacroDataRxTermMode, 13:12, scratch83, 31:30); + s(EmcDataBrlshft1, 23:0, scratch84, 23:0); + s(McEmemArbTimingRc, 7:0, scratch84, 31:24); + s(EmcDqsBrlshft0, 23:0, scratch85, 23:0); + s(McEmemArbRsv, 7:0, scratch85, 31:24); + s(EmcDqsBrlshft1, 23:0, scratch86, 23:0); + s(EmcCfgPipe2, 11:0, scratch87, 11:0); + s(EmcCfgPipe2, 27:16, scratch87, 23:12); + s(EmcCfgPipe1, 11:0, scratch88, 11:0); + s(EmcCfgPipe1, 27:16, scratch88, 23:12); + s(EmcPmacroCmdCtrl0, 5:0, scratch89, 5:0); + s(EmcPmacroCmdCtrl0, 13:8, scratch89, 11:6); + s(EmcPmacroCmdCtrl0, 21:16, scratch89, 17:12); + s(EmcPmacroCmdCtrl0, 29:24, scratch89, 23:18); + s(EmcPmacroCmdCtrl1, 5:0, scratch90, 5:0); + s(EmcPmacroCmdCtrl1, 13:8, scratch90, 11:6); + s(EmcPmacroCmdCtrl1, 21:16, scratch90, 17:12); + s(EmcPmacroCmdCtrl1, 29:24, scratch90, 23:18); + s(EmcRas, 6:0, scratch90, 30:24); + s(EmcCfg, 8:8, scratch90, 31:31); + s(EmcPmacroVttgenCtrl2, 23:0, scratch91, 23:0); + s(EmcW2p, 6:0, scratch91, 30:24); + s(EmcCfg, 9:9, scratch91, 31:31); + s(EmcPmacroCmdPadRxCtrl, 2:0, scratch92, 2:0); + s(EmcPmacroCmdPadRxCtrl, 5:4, scratch92, 4:3); + s(EmcPmacroCmdPadRxCtrl, 10:8, scratch92, 7:5); + s(EmcPmacroCmdPadRxCtrl, 22:12, scratch92, 18:8); + s(EmcPmacroCmdPadRxCtrl, 28:24, scratch92, 23:19); + s(EmcQSafe, 6:0, scratch92, 30:24); + s(EmcCfg, 18:18, scratch92, 31:31); + s(EmcPmacroDataPadRxCtrl, 2:0, scratch93, 2:0); + s(EmcPmacroDataPadRxCtrl, 5:4, scratch93, 4:3); + s(EmcPmacroDataPadRxCtrl, 10:8, scratch93, 7:5); + s(EmcPmacroDataPadRxCtrl, 22:12, scratch93, 18:8); + s(EmcPmacroDataPadRxCtrl, 28:24, scratch93, 23:19); + s(EmcRdv, 6:0, scratch93, 30:24); + s(EmcCfg, 21:21, scratch93, 31:31); + s(McEmemArbDaCovers, 23:0, scratch94, 23:0); + s(EmcRw2Pden, 6:0, scratch94, 30:24); + s(EmcCfg, 22:22, scratch94, 31:31); + s(EmcPmacroCmdCtrl2, 5:0, scratch95, 5:0); + s(EmcPmacroCmdCtrl2, 13:9, scratch95, 10:6); + s(EmcPmacroCmdCtrl2, 21:16, scratch95, 16:11); + s(EmcPmacroCmdCtrl2, 29:24, scratch95, 22:17); + s(EmcRfcPb, 8:0, scratch95, 31:23); + s(EmcPmacroQuseDdllRank0_0, 10:0, scratch96, 10:0); + s(EmcPmacroQuseDdllRank0_0, 26:16, scratch96, 21:11); + s(EmcCfgUpdate, 2:0, scratch96, 24:22); + s(EmcCfgUpdate, 10:8, scratch96, 27:25); + s(EmcCfgUpdate, 31:28, scratch96, 31:28); + s(EmcPmacroQuseDdllRank0_1, 10:0, scratch97, 10:0); + s(EmcPmacroQuseDdllRank0_1, 26:16, scratch97, 21:11); + s(EmcRfc, 9:0, scratch97, 31:22); + s(EmcPmacroQuseDdllRank0_2, 10:0, scratch98, 10:0); + s(EmcPmacroQuseDdllRank0_2, 26:16, scratch98, 21:11); + s(EmcTxsr, 9:0, scratch98, 31:22); + s(EmcPmacroQuseDdllRank0_3, 10:0, scratch99, 10:0); + s(EmcPmacroQuseDdllRank0_3, 26:16, scratch99, 21:11); + s(EmcMc2EmcQ, 2:0, scratch99, 24:22); + s(EmcMc2EmcQ, 10:8, scratch99, 27:25); + s(EmcMc2EmcQ, 27:24, scratch99, 31:28); + s(EmcPmacroQuseDdllRank0_4, 10:0, scratch100, 10:0); + s(EmcPmacroQuseDdllRank0_4, 26:16, scratch100, 21:11); + s(McEmemArbRing1Throttle, 4:0, scratch100, 26:22); + s(McEmemArbRing1Throttle, 20:16, scratch100, 31:27); + s(EmcPmacroQuseDdllRank0_5, 10:0, scratch101, 10:0); + s(EmcPmacroQuseDdllRank0_5, 26:16, scratch101, 21:11); + s(EmcPmacroQuseDdllRank1_0, 10:0, scratch102, 10:0); + s(EmcPmacroQuseDdllRank1_0, 26:16, scratch102, 21:11); + s(EmcAr2Pden, 8:0, scratch102, 30:22); + s(EmcCfg, 23:23, scratch102, 31:31); + s(EmcPmacroQuseDdllRank1_1, 10:0, scratch103, 10:0); + s(EmcPmacroQuseDdllRank1_1, 26:16, scratch103, 21:11); + s(EmcRfcSlr, 8:0, scratch103, 30:22); + s(EmcCfg, 24:24, scratch103, 31:31); + s(EmcPmacroQuseDdllRank1_2, 10:0, scratch104, 10:0); + s(EmcPmacroQuseDdllRank1_2, 26:16, scratch104, 21:11); + s(EmcIbdly, 6:0, scratch104, 28:22); + s(EmcIbdly, 29:28, scratch104, 30:29); + s(EmcCfg, 25:25, scratch104, 31:31); + s(EmcPmacroQuseDdllRank1_3, 10:0, scratch105, 10:0); + s(EmcPmacroQuseDdllRank1_3, 26:16, scratch105, 21:11); + s(McEmemArbTimingRFCPB, 8:0, scratch105, 30:22); + s(EmcCfg, 26:26, scratch105, 31:31); + s(EmcPmacroQuseDdllRank1_4, 10:0, scratch106, 10:0); + s(EmcPmacroQuseDdllRank1_4, 26:16, scratch106, 21:11); + s(EmcTfaw, 6:0, scratch106, 28:22); + s(EmcPmacroDataPadTxCtrl, 3:2, scratch106, 30:29); + s(EmcCfg, 28:28, scratch106, 31:31); + s(EmcPmacroQuseDdllRank1_5, 10:0, scratch107, 10:0); + s(EmcPmacroQuseDdllRank1_5, 26:16, scratch107, 21:11); + s(EmcTClkStable, 6:0, scratch107, 28:22); + s(EmcPmacroDataPadTxCtrl, 7:6, scratch107, 30:29); + s(EmcCfg, 29:29, scratch107, 31:31); + s(EmcPmacroObDdllLongDqRank0_0, 10:0, scratch108, 10:0); + s(EmcPmacroObDdllLongDqRank0_0, 26:16, scratch108, 21:11); + s(EmcPdex2Mrr, 6:0, scratch108, 28:22); + s(EmcPmacroDataPadTxCtrl, 11:10, scratch108, 30:29); + s(EmcCfg, 30:30, scratch108, 31:31); + s(EmcPmacroObDdllLongDqRank0_1, 10:0, scratch109, 10:0); + s(EmcPmacroObDdllLongDqRank0_1, 26:16, scratch109, 21:11); + s(EmcRdvMask, 6:0, scratch109, 28:22); + s(EmcPmacroDataPadTxCtrl, 15:14, scratch109, 30:29); + s(EmcCfg, 31:31, scratch109, 31:31); + s(EmcPmacroObDdllLongDqRank0_2, 10:0, scratch110, 10:0); + s(EmcPmacroObDdllLongDqRank0_2, 26:16, scratch110, 21:11); + s(EmcRdvEarlyMask, 6:0, scratch110, 28:22); + s(EmcFbioCfg5, 4:4, scratch110, 29:29); + s(EmcFbioCfg5, 8:8, scratch110, 30:30); + s(EmcFbioCfg5, 10:10, scratch110, 31:31); + s(EmcPmacroObDdllLongDqRank0_3, 10:0, scratch111, 10:0); + s(EmcPmacroObDdllLongDqRank0_3, 26:16, scratch111, 21:11); + s(EmcRdvEarly, 6:0, scratch111, 28:22); + s(EmcFbioCfg5, 12:12, scratch111, 29:29); + s(EmcFbioCfg5, 25:24, scratch111, 31:30); + s(EmcPmacroObDdllLongDqRank0_4, 10:0, scratch112, 10:0); + s(EmcPmacroObDdllLongDqRank0_4, 26:16, scratch112, 21:11); + s(EmcPmacroDdllShortCmd_2, 6:0, scratch112, 28:22); + s(EmcFbioCfg5, 28:26, scratch112, 31:29); + s(EmcPmacroObDdllLongDqRank0_5, 10:0, scratch113, 10:0); + s(EmcPmacroObDdllLongDqRank0_5, 26:16, scratch113, 21:11); + s(McEmemArbTimingRp, 6:0, scratch113, 28:22); + s(EmcFbioCfg5, 31:30, scratch113, 30:29); + s(EmcCfg2, 0:0, scratch113, 31:31); + s(EmcPmacroObDdllLongDqRank1_0, 10:0, scratch114, 10:0); + s(EmcPmacroObDdllLongDqRank1_0, 26:16, scratch114, 21:11); + s(McEmemArbTimingRas, 6:0, scratch114, 28:22); + s(EmcCfg2, 2:1, scratch114, 30:29); + s(EmcCfg2, 7:7, scratch114, 31:31); + s(EmcPmacroObDdllLongDqRank1_1, 10:0, scratch115, 10:0); + s(EmcPmacroObDdllLongDqRank1_1, 26:16, scratch115, 21:11); + s(McEmemArbTimingFaw, 6:0, scratch115, 28:22); + s(EmcCfg2, 11:10, scratch115, 30:29); + s(EmcCfg2, 14:14, scratch115, 31:31); + s(EmcPmacroObDdllLongDqRank1_2, 10:0, scratch123, 10:0); + s(EmcPmacroObDdllLongDqRank1_2, 26:16, scratch123, 21:11); + s(McEmemArbTimingRap2Pre, 6:0, scratch123, 28:22); + s(EmcCfg2, 16:15, scratch123, 30:29); + s(EmcCfg2, 20:20, scratch123, 31:31); + s(EmcPmacroObDdllLongDqRank1_3, 10:0, scratch124, 10:0); + s(EmcPmacroObDdllLongDqRank1_3, 26:16, scratch124, 21:11); + s(McEmemArbTimingWap2Pre, 6:0, scratch124, 28:22); + s(EmcCfg2, 24:22, scratch124, 31:29); + s(EmcPmacroObDdllLongDqRank1_4, 10:0, scratch125, 10:0); + s(EmcPmacroObDdllLongDqRank1_4, 26:16, scratch125, 21:11); + s(McEmemArbTimingR2W, 6:0, scratch125, 28:22); + s(EmcCfg2, 25:25, scratch125, 29:29); + s(EmcCfg2, 29:28, scratch125, 31:30); + s(EmcPmacroObDdllLongDqRank1_5, 10:0, scratch126, 10:0); + s(EmcPmacroObDdllLongDqRank1_5, 26:16, scratch126, 21:11); + s(McEmemArbTimingW2R, 6:0, scratch126, 28:22); + s(EmcCfg2, 31:30, scratch126, 30:29); + s(EmcCfgPipe, 0:0, scratch126, 31:31); + s(EmcPmacroObDdllLongDqsRank0_0, 10:0, scratch127, 10:0); + s(EmcPmacroObDdllLongDqsRank0_0, 26:16, scratch127, 21:11); + s(EmcRp, 5:0, scratch127, 27:22); + s(EmcCfgPipe, 4:1, scratch127, 31:28); + s(EmcPmacroObDdllLongDqsRank0_1, 10:0, scratch128, 10:0); + s(EmcPmacroObDdllLongDqsRank0_1, 26:16, scratch128, 21:11); + s(EmcR2w, 5:0, scratch128, 27:22); + s(EmcCfgPipe, 8:5, scratch128, 31:28); + s(EmcPmacroObDdllLongDqsRank0_2, 10:0, scratch129, 10:0); + s(EmcPmacroObDdllLongDqsRank0_2, 26:16, scratch129, 21:11); + s(EmcW2r, 5:0, scratch129, 27:22); + s(EmcCfgPipe, 11:9, scratch129, 30:28); + s(EmcCfgPipe, 16:16, scratch129, 31:31); + s(EmcPmacroObDdllLongDqsRank0_3, 10:0, scratch130, 10:0); + s(EmcPmacroObDdllLongDqsRank0_3, 26:16, scratch130, 21:11); + s(EmcR2p, 5:0, scratch130, 27:22); + s(EmcCfgPipe, 20:17, scratch130, 31:28); + s(EmcPmacroObDdllLongDqsRank0_4, 10:0, scratch131, 10:0); + s(EmcPmacroObDdllLongDqsRank0_4, 26:16, scratch131, 21:11); + s(EmcCcdmw, 5:0, scratch131, 27:22); + s(EmcCfgPipe, 24:21, scratch131, 31:28); + s(EmcPmacroObDdllLongDqsRank0_5, 10:0, scratch132, 10:0); + s(EmcPmacroObDdllLongDqsRank0_5, 26:16, scratch132, 21:11); + s(EmcRdRcd, 5:0, scratch132, 27:22); + s(EmcCfgPipe, 27:25, scratch132, 30:28); + s(EmcPmacroTxPwrd0, 0:0, scratch132, 31:31); + s(EmcPmacroObDdllLongDqsRank1_0, 10:0, scratch133, 10:0); + s(EmcPmacroObDdllLongDqsRank1_0, 26:16, scratch133, 21:11); + s(EmcWrRcd, 5:0, scratch133, 27:22); + s(EmcPmacroTxPwrd0, 4:1, scratch133, 31:28); + s(EmcPmacroObDdllLongDqsRank1_1, 10:0, scratch134, 10:0); + s(EmcPmacroObDdllLongDqsRank1_1, 26:16, scratch134, 21:11); + s(EmcWdv, 5:0, scratch134, 27:22); + s(EmcPmacroTxPwrd0, 8:5, scratch134, 31:28); + s(EmcPmacroObDdllLongDqsRank1_2, 10:0, scratch135, 10:0); + s(EmcPmacroObDdllLongDqsRank1_2, 26:16, scratch135, 21:11); + s(EmcQUse, 5:0, scratch135, 27:22); + s(EmcPmacroTxPwrd0, 12:9, scratch135, 31:28); + s(EmcPmacroObDdllLongDqsRank1_3, 10:0, scratch136, 10:0); + s(EmcPmacroObDdllLongDqsRank1_3, 26:16, scratch136, 21:11); + s(EmcPdEx2Wr, 5:0, scratch136, 27:22); + s(EmcPmacroTxPwrd0, 13:13, scratch136, 28:28); + s(EmcPmacroTxPwrd0, 18:16, scratch136, 31:29); + s(EmcPmacroObDdllLongDqsRank1_4, 10:0, scratch137, 10:0); + s(EmcPmacroObDdllLongDqsRank1_4, 26:16, scratch137, 21:11); + s(EmcPdEx2Rd, 5:0, scratch137, 27:22); + s(EmcPmacroTxPwrd0, 22:19, scratch137, 31:28); + s(EmcPmacroObDdllLongDqsRank1_5, 10:0, scratch138, 10:0); + s(EmcPmacroObDdllLongDqsRank1_5, 26:16, scratch138, 21:11); + s(EmcPdex2Cke, 5:0, scratch138, 27:22); + s(EmcPmacroTxPwrd0, 26:23, scratch138, 31:28); + s(EmcPmacroIbDdllLongDqsRank0_0, 10:0, scratch139, 10:0); + s(EmcPmacroIbDdllLongDqsRank0_0, 26:16, scratch139, 21:11); + s(EmcPChg2Pden, 5:0, scratch139, 27:22); + s(EmcPmacroTxPwrd0, 29:27, scratch139, 30:28); + s(EmcPmacroTxPwrd1, 0:0, scratch139, 31:31); + s(EmcPmacroIbDdllLongDqsRank0_1, 10:0, scratch140, 10:0); + s(EmcPmacroIbDdllLongDqsRank0_1, 26:16, scratch140, 21:11); + s(EmcAct2Pden, 5:0, scratch140, 27:22); + s(EmcPmacroTxPwrd1, 4:1, scratch140, 31:28); + s(EmcPmacroIbDdllLongDqsRank0_2, 10:0, scratch141, 10:0); + s(EmcPmacroIbDdllLongDqsRank0_2, 26:16, scratch141, 21:11); + s(EmcCke2Pden, 5:0, scratch141, 27:22); + s(EmcPmacroTxPwrd1, 8:5, scratch141, 31:28); + s(EmcPmacroIbDdllLongDqsRank0_3, 10:0, scratch142, 10:0); + s(EmcPmacroIbDdllLongDqsRank0_3, 26:16, scratch142, 21:11); + s(EmcTcke, 5:0, scratch142, 27:22); + s(EmcPmacroTxPwrd1, 12:9, scratch142, 31:28); + s(EmcPmacroIbDdllLongDqsRank1_0, 10:0, scratch143, 10:0); + s(EmcPmacroIbDdllLongDqsRank1_0, 26:16, scratch143, 21:11); + s(EmcTrpab, 5:0, scratch143, 27:22); + s(EmcPmacroTxPwrd1, 13:13, scratch143, 28:28); + s(EmcPmacroTxPwrd1, 18:16, scratch143, 31:29); + s(EmcPmacroIbDdllLongDqsRank1_1, 10:0, scratch144, 10:0); + s(EmcPmacroIbDdllLongDqsRank1_1, 26:16, scratch144, 21:11); + s(EmcClkenOverride, 3:1, scratch144, 24:22); + s(EmcClkenOverride, 8:6, scratch144, 27:25); + s(EmcPmacroTxPwrd1, 22:19, scratch144, 31:28); + s(EmcPmacroIbDdllLongDqsRank1_2, 10:0, scratch145, 10:0); + s(EmcPmacroIbDdllLongDqsRank1_2, 26:16, scratch145, 21:11); + s(EmcEInput, 5:0, scratch145, 27:22); + s(EmcPmacroTxPwrd1, 26:23, scratch145, 31:28); + s(EmcPmacroIbDdllLongDqsRank1_3, 10:0, scratch146, 10:0); + s(EmcPmacroIbDdllLongDqsRank1_3, 26:16, scratch146, 21:11); + s(EmcEInputDuration, 5:0, scratch146, 27:22); + s(EmcPmacroTxPwrd1, 29:27, scratch146, 30:28); + s(EmcPmacroTxPwrd2, 0:0, scratch146, 31:31); + s(EmcPmacroDdllLongCmd_0, 10:0, scratch147, 10:0); + s(EmcPmacroDdllLongCmd_0, 26:16, scratch147, 21:11); + s(EmcPutermExtra, 5:0, scratch147, 27:22); + s(EmcPmacroTxPwrd2, 4:1, scratch147, 31:28); + s(EmcPmacroDdllLongCmd_1, 10:0, scratch148, 10:0); + s(EmcPmacroDdllLongCmd_1, 26:16, scratch148, 21:11); + s(EmcTckesr, 5:0, scratch148, 27:22); + s(EmcPmacroTxPwrd2, 8:5, scratch148, 31:28); + s(EmcPmacroDdllLongCmd_2, 10:0, scratch149, 10:0); + s(EmcPmacroDdllLongCmd_2, 26:16, scratch149, 21:11); + s(EmcTpd, 5:0, scratch149, 27:22); + s(EmcPmacroTxPwrd2, 12:9, scratch149, 31:28); + s(EmcPmacroDdllLongCmd_3, 10:0, scratch150, 10:0); + s(EmcPmacroDdllLongCmd_3, 26:16, scratch150, 21:11); + s(EmcWdvMask, 5:0, scratch150, 27:22); + s(EmcPmacroTxPwrd2, 13:13, scratch150, 28:28); + s(EmcPmacroTxPwrd2, 18:16, scratch150, 31:29); + s(McEmemArbCfg, 8:0, scratch151, 8:0); + s(McEmemArbCfg, 20:16, scratch151, 13:9); + s(McEmemArbCfg, 31:24, scratch151, 21:14); + s(EmcWdvChk, 5:0, scratch151, 27:22); + s(EmcPmacroTxPwrd2, 22:19, scratch151, 31:28); + s(McEmemArbMisc1, 12:0, scratch152, 12:0); + s(McEmemArbMisc1, 25:21, scratch152, 17:13); + s(McEmemArbMisc1, 31:28, scratch152, 21:18); + s(EmcCmdBrlshft0, 5:0, scratch152, 27:22); + s(EmcPmacroTxPwrd2, 26:23, scratch152, 31:28); + s(EmcMrsWaitCnt2, 9:0, scratch153, 9:0); + s(EmcMrsWaitCnt2, 26:16, scratch153, 20:10); + s(EmcPmacroIbRxrt, 10:0, scratch153, 31:21); + s(EmcMrsWaitCnt, 9:0, scratch154, 9:0); + s(EmcMrsWaitCnt, 26:16, scratch154, 20:10); + s(EmcPmacroDdllLongCmd_4, 10:0, scratch154, 31:21); + s(EmcAutoCalInterval, 20:0, scratch155, 20:0); + s(McEmemArbOutstandingReq, 8:0, scratch155, 29:21); + s(McEmemArbOutstandingReq, 31:30, scratch155, 31:30); + s(McEmemArbRefpbHpCtrl, 6:0, scratch156, 6:0); + s(McEmemArbRefpbHpCtrl, 14:8, scratch156, 13:7); + s(McEmemArbRefpbHpCtrl, 22:16, scratch156, 20:14); + s(EmcCmdBrlshft1, 5:0, scratch156, 26:21); + s(EmcRrd, 4:0, scratch156, 31:27); + s(EmcQuseBrlshft0, 19:0, scratch157, 19:0); + s(EmcFbioCfg8, 27:16, scratch157, 31:20); + s(EmcQuseBrlshft1, 19:0, scratch158, 19:0); + s(EmcTxsrDll, 11:0, scratch158, 31:20); + s(EmcQuseBrlshft2, 19:0, scratch159, 19:0); + s(EmcTxdsrvttgen, 11:0, scratch159, 31:20); + s(EmcQuseBrlshft3, 19:0, scratch160, 19:0); + s(EmcPmacroVttgenCtrl0, 3:0, scratch160, 23:20); + s(EmcPmacroVttgenCtrl0, 11:8, scratch160, 27:24); + s(EmcPmacroVttgenCtrl0, 19:16, scratch160, 31:28); + s(EmcPmacroVttgenCtrl1, 19:0, scratch161, 19:0); + s(EmcCmdBrlshft2, 5:0, scratch161, 25:20); + s(EmcCmdBrlshft3, 5:0, scratch161, 31:26); + s(EmcAutoCalConfig3, 5:0, scratch162, 5:0); + s(EmcAutoCalConfig3, 13:8, scratch162, 11:6); + s(EmcAutoCalConfig3, 18:16, scratch162, 14:12); + s(EmcAutoCalConfig3, 22:20, scratch162, 17:15); + s(EmcTRefBw, 13:0, scratch162, 31:18); + s(EmcAutoCalConfig4, 5:0, scratch163, 5:0); + s(EmcAutoCalConfig4, 13:8, scratch163, 11:6); + s(EmcAutoCalConfig4, 18:16, scratch163, 14:12); + s(EmcAutoCalConfig4, 22:20, scratch163, 17:15); + s(EmcQpop, 6:0, scratch163, 24:18); + s(EmcQpop, 22:16, scratch163, 31:25); + s(EmcAutoCalConfig5, 5:0, scratch164, 5:0); + s(EmcAutoCalConfig5, 13:8, scratch164, 11:6); + s(EmcAutoCalConfig5, 18:16, scratch164, 14:12); + s(EmcAutoCalConfig5, 22:20, scratch164, 17:15); + s(EmcPmacroAutocalCfgCommon, 5:0, scratch164, 23:18); + s(EmcPmacroAutocalCfgCommon, 13:8, scratch164, 29:24); + s(EmcPmacroAutocalCfgCommon, 16:16, scratch164, 30:30); + s(EmcPmacroTxPwrd2, 27:27, scratch164, 31:31); + s(EmcAutoCalConfig6, 5:0, scratch165, 5:0); + s(EmcAutoCalConfig6, 13:8, scratch165, 11:6); + s(EmcAutoCalConfig6, 18:16, scratch165, 14:12); + s(EmcAutoCalConfig6, 22:20, scratch165, 17:15); + s(EmcWev, 5:0, scratch165, 23:18); + s(EmcWsv, 5:0, scratch165, 29:24); + s(EmcPmacroTxPwrd2, 29:28, scratch165, 31:30); + s(EmcAutoCalConfig7, 5:0, scratch166, 5:0); + s(EmcAutoCalConfig7, 13:8, scratch166, 11:6); + s(EmcAutoCalConfig7, 18:16, scratch166, 14:12); + s(EmcAutoCalConfig7, 22:20, scratch166, 17:15); + s(EmcCfg3, 2:0, scratch166, 20:18); + s(EmcCfg3, 6:4, scratch166, 23:21); + s(EmcQuseWidth, 3:0, scratch166, 27:24); + s(EmcQuseWidth, 29:28, scratch166, 29:28); + s(EmcPmacroTxPwrd3, 1:0, scratch166, 31:30); + s(EmcAutoCalConfig8, 5:0, scratch167, 5:0); + s(EmcAutoCalConfig8, 13:8, scratch167, 11:6); + s(EmcAutoCalConfig8, 18:16, scratch167, 14:12); + s(EmcAutoCalConfig8, 22:20, scratch167, 17:15); + s(EmcPmacroBgBiasCtrl0, 2:0, scratch167, 20:18); + s(EmcPmacroBgBiasCtrl0, 6:4, scratch167, 23:21); + s(McEmemArbTimingRcd, 5:0, scratch167, 29:24); + s(EmcPmacroTxPwrd3, 3:2, scratch167, 31:30); + s(EmcXm2CompPadCtrl2, 17:0, scratch168, 17:0); + s(McEmemArbTimingCcdmw, 5:0, scratch168, 23:18); + s(McEmemArbOverride, 27:27, scratch168, 24:24); + s(McEmemArbOverride, 26:26, scratch168, 25:25); + s(McEmemArbOverride, 16:16, scratch168, 26:26); + s(McEmemArbOverride, 10:10, scratch168, 27:27); + s(McEmemArbOverride, 4:4, scratch168, 28:28); + s(McEmemArbOverride, 3:3, scratch168, 29:29); + s(EmcPmacroTxPwrd3, 5:4, scratch168, 31:30); + s(EmcXm2CompPadCtrl3, 17:0, scratch169, 17:0); + s(EmcRext, 4:0, scratch169, 22:18); + s(EmcTClkStop, 4:0, scratch169, 27:23); + s(EmcPmacroTxPwrd3, 9:6, scratch169, 31:28); + s(EmcZcalWaitCnt, 10:0, scratch170, 10:0); + s(EmcZcalWaitCnt, 21:16, scratch170, 16:11); + s(EmcZcalWaitCnt, 31:31, scratch170, 17:17); + s(EmcWext, 4:0, scratch170, 22:18); + s(EmcRefctrl2, 0:0, scratch170, 23:23); + s(EmcRefctrl2, 26:24, scratch170, 26:24); + s(EmcRefctrl2, 31:31, scratch170, 27:27); + s(EmcPmacroTxPwrd3, 13:10, scratch170, 31:28); + s(EmcZcalMrwCmd, 7:0, scratch171, 7:0); + s(EmcZcalMrwCmd, 23:16, scratch171, 15:8); + s(EmcZcalMrwCmd, 31:30, scratch171, 17:16); + s(EmcWeDuration, 4:0, scratch171, 22:18); + s(EmcWsDuration, 4:0, scratch171, 27:23); + s(EmcPmacroTxPwrd3, 19:16, scratch171, 31:28); + s(EmcSwizzleRank0Byte0, 2:0, scratch172, 2:0); + s(EmcSwizzleRank0Byte0, 6:4, scratch172, 5:3); + s(EmcSwizzleRank0Byte0, 10:8, scratch172, 8:6); + s(EmcSwizzleRank0Byte0, 14:12, scratch172, 11:9); + s(EmcSwizzleRank0Byte0, 18:16, scratch172, 14:12); + s(EmcSwizzleRank0Byte0, 22:20, scratch172, 17:15); + s(EmcPutermWidth, 31:31, scratch172, 18:18); + s(EmcPutermWidth, 3:0, scratch172, 22:19); + s(McEmemArbTimingRrd, 4:0, scratch172, 27:23); + s(EmcPmacroTxPwrd3, 23:20, scratch172, 31:28); + s(EmcSwizzleRank0Byte1, 2:0, scratch173, 2:0); + s(EmcSwizzleRank0Byte1, 6:4, scratch173, 5:3); + s(EmcSwizzleRank0Byte1, 10:8, scratch173, 8:6); + s(EmcSwizzleRank0Byte1, 14:12, scratch173, 11:9); + s(EmcSwizzleRank0Byte1, 18:16, scratch173, 14:12); + s(EmcSwizzleRank0Byte1, 22:20, scratch173, 17:15); + s(McEmemArbTimingR2R, 4:0, scratch173, 22:18); + s(McEmemArbTimingW2W, 4:0, scratch173, 27:23); + s(EmcPmacroTxPwrd3, 27:24, scratch173, 31:28); + s(EmcSwizzleRank0Byte2, 2:0, scratch174, 2:0); + s(EmcSwizzleRank0Byte2, 6:4, scratch174, 5:3); + s(EmcSwizzleRank0Byte2, 10:8, scratch174, 8:6); + s(EmcSwizzleRank0Byte2, 14:12, scratch174, 11:9); + s(EmcSwizzleRank0Byte2, 18:16, scratch174, 14:12); + s(EmcSwizzleRank0Byte2, 22:20, scratch174, 17:15); + s(EmcPmacroTxPwrd3, 29:28, scratch174, 19:18); + s(EmcPmacroTxSelClkSrc0, 11:0, scratch174, 31:20); + s(EmcSwizzleRank0Byte3, 2:0, scratch175, 2:0); + s(EmcSwizzleRank0Byte3, 6:4, scratch175, 5:3); + s(EmcSwizzleRank0Byte3, 10:8, scratch175, 8:6); + s(EmcSwizzleRank0Byte3, 14:12, scratch175, 11:9); + s(EmcSwizzleRank0Byte3, 18:16, scratch175, 14:12); + s(EmcSwizzleRank0Byte3, 22:20, scratch175, 17:15); + s(EmcPmacroTxSelClkSrc0, 27:16, scratch175, 29:18); + s(EmcPmacroTxSelClkSrc1, 1:0, scratch175, 31:30); + s(EmcSwizzleRank1Byte0, 2:0, scratch176, 2:0); + s(EmcSwizzleRank1Byte0, 6:4, scratch176, 5:3); + s(EmcSwizzleRank1Byte0, 10:8, scratch176, 8:6); + s(EmcSwizzleRank1Byte0, 14:12, scratch176, 11:9); + s(EmcSwizzleRank1Byte0, 18:16, scratch176, 14:12); + s(EmcSwizzleRank1Byte0, 22:20, scratch176, 17:15); + s(EmcPmacroTxSelClkSrc1, 11:2, scratch176, 27:18); + s(EmcPmacroTxSelClkSrc1, 19:16, scratch176, 31:28); + s(EmcSwizzleRank1Byte1, 2:0, scratch177, 2:0); + s(EmcSwizzleRank1Byte1, 6:4, scratch177, 5:3); + s(EmcSwizzleRank1Byte1, 10:8, scratch177, 8:6); + s(EmcSwizzleRank1Byte1, 14:12, scratch177, 11:9); + s(EmcSwizzleRank1Byte1, 18:16, scratch177, 14:12); + s(EmcSwizzleRank1Byte1, 22:20, scratch177, 17:15); + s(EmcPmacroTxSelClkSrc1, 27:20, scratch177, 25:18); + s(EmcPmacroTxSelClkSrc3, 5:0, scratch177, 31:26); + s(EmcSwizzleRank1Byte2, 2:0, scratch178, 2:0); + s(EmcSwizzleRank1Byte2, 6:4, scratch178, 5:3); + s(EmcSwizzleRank1Byte2, 10:8, scratch178, 8:6); + s(EmcSwizzleRank1Byte2, 14:12, scratch178, 11:9); + s(EmcSwizzleRank1Byte2, 18:16, scratch178, 14:12); + s(EmcSwizzleRank1Byte2, 22:20, scratch178, 17:15); + s(EmcPmacroTxSelClkSrc3, 11:6, scratch178, 23:18); + s(EmcPmacroTxSelClkSrc3, 23:16, scratch178, 31:24); + s(EmcSwizzleRank1Byte3, 2:0, scratch179, 2:0); + s(EmcSwizzleRank1Byte3, 6:4, scratch179, 5:3); + s(EmcSwizzleRank1Byte3, 10:8, scratch179, 8:6); + s(EmcSwizzleRank1Byte3, 14:12, scratch179, 11:9); + s(EmcSwizzleRank1Byte3, 18:16, scratch179, 14:12); + s(EmcSwizzleRank1Byte3, 22:20, scratch179, 17:15); + s(EmcPmacroTxSelClkSrc3, 27:24, scratch179, 21:18); + s(EmcPmacroTxSelClkSrc2, 9:0, scratch179, 31:22); + s(EmcPmacroCmdBrickCtrlFdpd, 17:0, scratch180, 17:0); + s(EmcPmacroTxSelClkSrc2, 11:10, scratch180, 19:18); + s(EmcPmacroTxSelClkSrc2, 27:16, scratch180, 31:20); + s(EmcPmacroDataBrickCtrlFdpd, 17:0, scratch181, 17:0); + s(EmcPmacroTxSelClkSrc4, 11:0, scratch181, 29:18); + s(EmcPmacroTxSelClkSrc4, 17:16, scratch181, 31:30); + s(EmcFbioCfg7, 16:0, scratch182, 16:0); + s(McEmemArbRefpbBankCtrl, 6:0, scratch182, 23:17); + s(McEmemArbRefpbBankCtrl, 14:8, scratch182, 30:24); + s(McEmemArbRefpbBankCtrl, 31:31, scratch182, 31:31); + s(EmcDynSelfRefControl, 15:0, scratch183, 15:0); + s(EmcDynSelfRefControl, 31:31, scratch183, 16:16); + s(EmcPmacroTxSelClkSrc4, 27:18, scratch183, 26:17); + s(EmcPmacroTxSelClkSrc5, 4:0, scratch183, 31:27); + s(EmcDllCfg1, 16:0, scratch184, 16:0); + s(EmcPmacroTxSelClkSrc5, 11:5, scratch184, 23:17); + s(EmcPmacroTxSelClkSrc5, 23:16, scratch184, 31:24); + s(EmcPmacroPadCfgCtrl, 1:0, scratch185, 1:0); + s(EmcPmacroPadCfgCtrl, 6:5, scratch185, 3:2); + s(EmcPmacroPadCfgCtrl, 11:9, scratch185, 6:4); + s(EmcPmacroPadCfgCtrl, 13:13, scratch185, 7:7); + s(EmcPmacroPadCfgCtrl, 17:16, scratch185, 9:8); + s(EmcPmacroPadCfgCtrl, 21:20, scratch185, 11:10); + s(EmcPmacroPadCfgCtrl, 25:24, scratch185, 13:12); + s(EmcPmacroPadCfgCtrl, 30:28, scratch185, 16:14); + s(EmcPmacroTxSelClkSrc5, 27:24, scratch185, 20:17); + s(EmcPmacroCmdPadTxCtrl, 1:0, scratch185, 22:21); + s(EmcPmacroCmdPadTxCtrl, 5:4, scratch185, 24:23); + s(EmcPmacroCmdPadTxCtrl, 9:8, scratch185, 26:25); + s(EmcPmacroCmdPadTxCtrl, 13:12, scratch185, 28:27); + s(EmcPmacroCmdPadTxCtrl, 16:16, scratch185, 29:29); + s(EmcPmacroCmdPadTxCtrl, 21:20, scratch185, 31:30); + s(EmcRefresh, 15:0, scratch186, 15:0); + s(EmcCmdQ, 4:0, scratch186, 20:16); + s(EmcCmdQ, 10:8, scratch186, 23:21); + s(EmcCmdQ, 14:12, scratch186, 26:24); + s(EmcCmdQ, 28:24, scratch186, 31:27); + s(EmcAcpdControl, 15:0, scratch187, 15:0); + s(EmcAutoCalVrefSel1, 15:0, scratch187, 31:16); + s(EmcXm2CompPadCtrl, 1:0, scratch188, 1:0); + s(EmcXm2CompPadCtrl, 6:3, scratch188, 5:2); + s(EmcXm2CompPadCtrl, 9:9, scratch188, 6:6); + s(EmcXm2CompPadCtrl, 19:11, scratch188, 15:7); + s(EmcCfgDigDllPeriod, 15:0, scratch188, 31:16); + s(EmcCfgDigDll_1, 15:0, scratch189, 15:0); + s(EmcPreRefreshReqCnt, 15:0, scratch189, 31:16); + s(EmcPmacroCmdPadTxCtrl, 27:24, scratch190, 19:16); + s(EmcPmacroDataPadTxCtrl, 1:0, scratch190, 21:20); + s(EmcPmacroDataPadTxCtrl, 5:4, scratch190, 23:22); + s(EmcPmacroDataPadTxCtrl, 9:8, scratch190, 25:24); + s(EmcPmacroDataPadTxCtrl, 13:12, scratch190, 27:26); + s(EmcPmacroDataPadTxCtrl, 16:16, scratch190, 28:28); + s(EmcPmacroDataPadTxCtrl, 21:20, scratch190, 30:29); + s(EmcPmacroDataPadTxCtrl, 24:24, scratch190, 31:31); + s(EmcPmacroDataPadTxCtrl, 27:25, scratch191, 2:0); + + s(EmcPinGpio, 1:0, scratch8, 31:30); + s(EmcPinGpioEn, 1:0, scratch9, 31:30); + s(EmcDevSelect, 1:0, scratch10, 31:30); + s(EmcZcalWarmColdBootEnables, 1:0, scratch11, 31:30); + s(EmcCfgDigDllPeriodWarmBoot, 1:0, scratch12, 31:30); + s32(EmcBctSpare13, scratch31); + s32(EmcBctSpare12, scratch32); + s32(EmcBctSpare7, scratch33); + s32(EmcBctSpare6, scratch40); + s32(EmcBctSpare5, scratch42); + s32(EmcBctSpare4, scratch44); + s32(SwizzleRankByteEncode, scratch45); + //s32(EmcBctSpare2, scratch46); + pmc->scratch46 = 0x40000DD8; + s32(EmcBctSpare1, scratch47); + s32(EmcBctSpare0, scratch48); + s32(EmcBctSpare9, scratch50); + s32(EmcBctSpare8, scratch51); + s32(BootRomPatchData, scratch56); + s32(BootRomPatchControl, scratch57); + s(McClkenOverrideAllWarmBoot, 0:0, scratch58, 31:31); + s(EmcClkenOverrideAllWarmBoot, 0:0, scratch59, 30:30); + s(EmcMrsWarmBootEnable, 0:0, scratch59, 31:31); + s(ClearClk2Mc1, 0:0, scratch60, 30:30); + s(EmcWarmBootExtraModeRegWriteEnable, 0:0, scratch60, 31:31); + s(ClkRstControllerPllmMisc2OverrideEnable, 0:0, scratch61, 30:30); + s(EmcDbgWriteMux, 0:0, scratch61, 31:31); + s(EmcExtraRefreshNum, 2:0, scratch62, 31:29); + s(PmcIoDpd3ReqWait, 2:0, scratch68, 30:28); + s(AhbArbitrationXbarCtrlMemInitDone, 0:0, scratch68, 31:31); + s(MemoryType, 2:0, scratch69, 30:28); + s(PmcIoDpd4ReqWait, 2:0, scratch70, 30:28); + s(EmcTimingControlWait, 7:0, scratch86, 31:24); + s(EmcZcalWarmBootWait, 7:0, scratch87, 31:24); + s(WarmBootWait, 7:0, scratch88, 31:24); + s(EmcPinProgramWait, 7:0, scratch89, 31:24); + s(EmcAutoCalWait, 9:0, scratch101, 31:22); + s(SwizzleRankByteEncode, 15:0, scratch190, 15:0); + + switch (sdram->MemoryType) { + case NvBootMemoryType_LpDdr2: + case NvBootMemoryType_LpDdr4: + s(EmcMrwLpddr2ZcalWarmBoot, 23:16, scratch5, 7:0); + s(EmcMrwLpddr2ZcalWarmBoot, 7:0, scratch5, 15:8); + s(EmcWarmBootMrwExtra, 23:16, scratch5, 23:16); + s(EmcWarmBootMrwExtra, 7:0, scratch5, 31:24); + s(EmcMrwLpddr2ZcalWarmBoot, 31:30, scratch6, 1:0); + s(EmcWarmBootMrwExtra, 31:30, scratch6, 3:2); + s(EmcMrwLpddr2ZcalWarmBoot, 27:26, scratch6, 5:4); + s(EmcWarmBootMrwExtra, 27:26, scratch6, 7:6); + s(EmcMrw6, 27:0, scratch8, 27:0); + s(EmcMrw6, 31:30, scratch8, 29:28); + s(EmcMrw8, 27:0, scratch9, 27:0); + s(EmcMrw8, 31:30, scratch9, 29:28); + s(EmcMrw9, 27:0, scratch10, 27:0); + s(EmcMrw9, 31:30, scratch10, 29:28); + s(EmcMrw10, 27:0, scratch11, 27:0); + s(EmcMrw10, 31:30, scratch11, 29:28); + s(EmcMrw12, 27:0, scratch12, 27:0); + s(EmcMrw12, 31:30, scratch12, 29:28); + s(EmcMrw13, 27:0, scratch13, 27:0); + s(EmcMrw13, 31:30, scratch13, 29:28); + s(EmcMrw14, 27:0, scratch14, 27:0); + s(EmcMrw14, 31:30, scratch14, 29:28); + s(EmcMrw1, 7:0, scratch15, 7:0); + s(EmcMrw1, 23:16, scratch15, 15:8); + s(EmcMrw1, 27:26, scratch15, 17:16); + s(EmcMrw1, 31:30, scratch15, 19:18); + s(EmcWarmBootMrwExtra, 7:0, scratch16, 7:0); + s(EmcWarmBootMrwExtra, 23:16, scratch16, 15:8); + s(EmcWarmBootMrwExtra, 27:26, scratch16, 17:16); + s(EmcWarmBootMrwExtra, 31:30, scratch16, 19:18); + s(EmcMrw2, 7:0, scratch17, 7:0); + s(EmcMrw2, 23:16, scratch17, 15:8); + s(EmcMrw2, 27:26, scratch17, 17:16); + s(EmcMrw2, 31:30, scratch17, 19:18); + s(EmcMrw3, 7:0, scratch18, 7:0); + s(EmcMrw3, 23:16, scratch18, 15:8); + s(EmcMrw3, 27:26, scratch18, 17:16); + s(EmcMrw3, 31:30, scratch18, 19:18); + s(EmcMrw4, 7:0, scratch19, 7:0); + s(EmcMrw4, 23:16, scratch19, 15:8); + s(EmcMrw4, 27:26, scratch19, 17:16); + s(EmcMrw4, 31:30, scratch19, 19:18); + break; + case NvBootMemoryType_Ddr3: + s(EmcMrs, 13:0, scratch5, 13:0); + s(EmcEmrs, 13:0, scratch5, 27:14); + s(EmcMrs, 21:20, scratch5, 29:28); + s(EmcMrs, 31:30, scratch5, 31:30); + s(EmcEmrs2, 13:0, scratch8, 13:0); + s(EmcEmrs3, 13:0, scratch8, 27:14); + s(EmcEmrs, 21:20, scratch8, 29:28); + s(EmcWarmBootMrsExtra, 13:0, scratch9, 13:0); + s(EmcEmrs, 31:30, scratch9, 15:14); + s(EmcEmrs2, 21:20, scratch9, 17:16); + s(EmcEmrs2, 31:30, scratch9, 19:18); + s(EmcEmrs3, 21:20, scratch9, 21:20); + s(EmcEmrs3, 31:30, scratch9, 23:22); + s(EmcWarmBootMrsExtra, 31:30, scratch9, 25:24); + s(EmcWarmBootMrsExtra, 21:20, scratch9, 27:26); + s(EmcZqCalDdr3WarmBoot, 31:30, scratch9, 29:28); + s(EmcMrs, 27:26, scratch10, 1:0); + s(EmcEmrs, 27:26, scratch10, 3:2); + s(EmcEmrs2, 27:26, scratch10, 5:4); + s(EmcEmrs3, 27:26, scratch10, 7:6); + s(EmcWarmBootMrsExtra, 27:27, scratch10, 8:8); + s(EmcWarmBootMrsExtra, 26:26, scratch10, 9:9); + s(EmcZqCalDdr3WarmBoot, 0:0, scratch10, 10:10); + s(EmcZqCalDdr3WarmBoot, 4:4, scratch10, 11:11); + break; + } + + s32(EmcCmdMappingByte, secure_scratch8); + s32(EmcPmacroBrickMapping0, secure_scratch9); + s32(EmcPmacroBrickMapping1, secure_scratch10); + s32(EmcPmacroBrickMapping2, secure_scratch11); + s32(McVideoProtectGpuOverride0, secure_scratch12); + s(EmcCmdMappingCmd0_0, 6:0, secure_scratch13, 6:0); + s(EmcCmdMappingCmd0_0, 14:8, secure_scratch13, 13:7); + s(EmcCmdMappingCmd0_0, 22:16, secure_scratch13, 20:14); + s(EmcCmdMappingCmd0_0, 30:24, secure_scratch13, 27:21); + s(McVideoProtectBomAdrHi, 1:0, secure_scratch13, 29:28); + s(McVideoProtectWriteAccess, 1:0, secure_scratch13, 31:30); + s(EmcCmdMappingCmd0_1, 6:0, secure_scratch14, 6:0); + s(EmcCmdMappingCmd0_1, 14:8, secure_scratch14, 13:7); + s(EmcCmdMappingCmd0_1, 22:16, secure_scratch14, 20:14); + s(EmcCmdMappingCmd0_1, 30:24, secure_scratch14, 27:21); + s(McSecCarveoutAdrHi, 1:0, secure_scratch14, 29:28); + s(McMtsCarveoutAdrHi, 1:0, secure_scratch14, 31:30); + s(EmcCmdMappingCmd1_0, 6:0, secure_scratch15, 6:0); + s(EmcCmdMappingCmd1_0, 14:8, secure_scratch15, 13:7); + s(EmcCmdMappingCmd1_0, 22:16, secure_scratch15, 20:14); + s(EmcCmdMappingCmd1_0, 30:24, secure_scratch15, 27:21); + s(McGeneralizedCarveout5BomHi, 1:0, secure_scratch15, 29:28); + s(McGeneralizedCarveout3BomHi, 1:0, secure_scratch15, 31:30); + s(EmcCmdMappingCmd1_1, 6:0, secure_scratch16, 6:0); + s(EmcCmdMappingCmd1_1, 14:8, secure_scratch16, 13:7); + s(EmcCmdMappingCmd1_1, 22:16, secure_scratch16, 20:14); + s(EmcCmdMappingCmd1_1, 30:24, secure_scratch16, 27:21); + s(McGeneralizedCarveout2BomHi, 1:0, secure_scratch16, 29:28); + s(McGeneralizedCarveout4BomHi, 1:0, secure_scratch16, 31:30); + s(EmcCmdMappingCmd2_0, 6:0, secure_scratch17, 6:0); + s(EmcCmdMappingCmd2_0, 14:8, secure_scratch17, 13:7); + s(EmcCmdMappingCmd2_0, 22:16, secure_scratch17, 20:14); + s(EmcCmdMappingCmd2_0, 30:24, secure_scratch17, 27:21); + s(McGeneralizedCarveout1BomHi, 1:0, secure_scratch17, 29:28); + s(EmcAdrCfg, 0:0, secure_scratch17, 30:30); + s(EmcFbioSpare, 1:1, secure_scratch17, 31:31); + s(EmcCmdMappingCmd2_1, 6:0, secure_scratch18, 6:0); + s(EmcCmdMappingCmd2_1, 14:8, secure_scratch18, 13:7); + s(EmcCmdMappingCmd2_1, 22:16, secure_scratch18, 20:14); + s(EmcCmdMappingCmd2_1, 30:24, secure_scratch18, 27:21); + s(EmcFbioCfg8, 15:15, secure_scratch18, 28:28); + s(McEmemAdrCfg, 0:0, secure_scratch18, 29:29); + s(McSecCarveoutProtectWriteAccess, 0:0, secure_scratch18, 30:30); + s(McMtsCarveoutRegCtrl, 0:0, secure_scratch18, 31:31); + s(EmcCmdMappingCmd3_0, 6:0, secure_scratch19, 6:0); + s(EmcCmdMappingCmd3_0, 14:8, secure_scratch19, 13:7); + s(EmcCmdMappingCmd3_0, 22:16, secure_scratch19, 20:14); + s(EmcCmdMappingCmd3_0, 30:24, secure_scratch19, 27:21); + s(McGeneralizedCarveout2Cfg0, 6:3, secure_scratch19, 31:28); + s(EmcCmdMappingCmd3_1, 6:0, secure_scratch20, 6:0); + s(EmcCmdMappingCmd3_1, 14:8, secure_scratch20, 13:7); + s(EmcCmdMappingCmd3_1, 22:16, secure_scratch20, 20:14); + s(EmcCmdMappingCmd3_1, 30:24, secure_scratch20, 27:21); + s(McGeneralizedCarveout2Cfg0, 10:7, secure_scratch20, 31:28); + s(McGeneralizedCarveout4Cfg0, 26:0, secure_scratch39, 26:0); + s(McGeneralizedCarveout2Cfg0, 17:14, secure_scratch39, 30:27); + s(McVideoProtectVprOverride, 0:0, secure_scratch39, 31:31); + s(McGeneralizedCarveout5Cfg0, 26:0, secure_scratch40, 26:0); + s(McGeneralizedCarveout2Cfg0, 21:18, secure_scratch40, 30:27); + s(McVideoProtectVprOverride, 1:1, secure_scratch40, 31:31); + s(EmcCmdMappingCmd0_2, 6:0, secure_scratch41, 6:0); + s(EmcCmdMappingCmd0_2, 14:8, secure_scratch41, 13:7); + s(EmcCmdMappingCmd0_2, 22:16, secure_scratch41, 20:14); + s(EmcCmdMappingCmd0_2, 27:24, secure_scratch41, 24:21); + s(McGeneralizedCarveout1Cfg0, 6:3, secure_scratch41, 28:25); + s(McGeneralizedCarveout2Cfg0, 13:11, secure_scratch41, 31:29); + s(EmcCmdMappingCmd1_2, 6:0, secure_scratch42, 6:0); + s(EmcCmdMappingCmd1_2, 14:8, secure_scratch42, 13:7); + s(EmcCmdMappingCmd1_2, 22:16, secure_scratch42, 20:14); + s(EmcCmdMappingCmd1_2, 27:24, secure_scratch42, 24:21); + s(McGeneralizedCarveout1Cfg0, 13:7, secure_scratch42, 31:25); + s(EmcCmdMappingCmd2_2, 6:0, secure_scratch43, 6:0); + s(EmcCmdMappingCmd2_2, 14:8, secure_scratch43, 13:7); + s(EmcCmdMappingCmd2_2, 22:16, secure_scratch43, 20:14); + s(EmcCmdMappingCmd2_2, 27:24, secure_scratch43, 24:21); + s(McGeneralizedCarveout1Cfg0, 17:14, secure_scratch43, 28:25); + s(McGeneralizedCarveout3Cfg0, 13:11, secure_scratch43, 31:29); + s(EmcCmdMappingCmd3_2, 6:0, secure_scratch44, 6:0); + s(EmcCmdMappingCmd3_2, 14:8, secure_scratch44, 13:7); + s(EmcCmdMappingCmd3_2, 22:16, secure_scratch44, 20:14); + s(EmcCmdMappingCmd3_2, 27:24, secure_scratch44, 24:21); + s(McGeneralizedCarveout1Cfg0, 21:18, secure_scratch44, 28:25); + s(McVideoProtectVprOverride, 3:2, secure_scratch44, 30:29); + s(McVideoProtectVprOverride, 6:6, secure_scratch44, 31:31); + s(McEmemAdrCfgChannelMask, 31:9, secure_scratch45, 22:0); + s(McEmemAdrCfgDev0, 2:0, secure_scratch45, 25:23); + s(McEmemAdrCfgDev0, 9:8, secure_scratch45, 27:26); + s(McEmemAdrCfgDev0, 19:16, secure_scratch45, 31:28); + s(McEmemAdrCfgBankMask0, 31:10, secure_scratch46, 21:0); + s(McEmemAdrCfgDev1, 2:0, secure_scratch46, 24:22); + s(McEmemAdrCfgDev1, 9:8, secure_scratch46, 26:25); + s(McEmemAdrCfgDev1, 19:16, secure_scratch46, 30:27); + s(McVideoProtectVprOverride, 7:7, secure_scratch46, 31:31); + s(McEmemAdrCfgBankMask1, 31:10, secure_scratch47, 21:0); + s(McGeneralizedCarveout3Cfg0, 10:3, secure_scratch47, 29:22); + s(McVideoProtectVprOverride, 9:8, secure_scratch47, 31:30); + s(McEmemAdrCfgBankMask2, 31:10, secure_scratch48, 21:0); + s(McGeneralizedCarveout3Cfg0, 21:14, secure_scratch48, 29:22); + s(McVideoProtectVprOverride, 11:11, secure_scratch48, 30:30); + s(McVideoProtectVprOverride, 14:14, secure_scratch48, 31:31); + s(McVideoProtectGpuOverride1, 15:0, secure_scratch49, 15:0); + s(McEmemCfg, 13:0, secure_scratch49, 29:16); + s(McEmemCfg, 31:31, secure_scratch49, 30:30); + s(McVideoProtectVprOverride, 15:15, secure_scratch49, 31:31); + s(McGeneralizedCarveout3Bom, 31:17, secure_scratch50, 14:0); + s(McGeneralizedCarveout1Bom, 31:17, secure_scratch50, 29:15); + s(McVideoProtectVprOverride, 18:17, secure_scratch50, 31:30); + s(McGeneralizedCarveout4Bom, 31:17, secure_scratch51, 14:0); + s(McGeneralizedCarveout2Bom, 31:17, secure_scratch51, 29:15); + s(McVideoProtectVprOverride, 20:19, secure_scratch51, 31:30); + s(McGeneralizedCarveout5Bom, 31:17, secure_scratch52, 14:0); + s(McVideoProtectBom, 31:20, secure_scratch52, 26:15); + s(McVideoProtectVprOverride, 23:21, secure_scratch52, 29:27); + s(McVideoProtectVprOverride, 26:26, secure_scratch52, 30:30); + s(McVideoProtectVprOverride, 29:29, secure_scratch52, 31:31); + s(McVideoProtectSizeMb, 11:0, secure_scratch53, 11:0); + s(McSecCarveoutBom, 31:20, secure_scratch53, 23:12); + s(McVideoProtectVprOverride, 31:30, secure_scratch53, 25:24); + s(McVideoProtectVprOverride1, 1:0, secure_scratch53, 27:26); + s(McVideoProtectVprOverride1, 7:4, secure_scratch53, 31:28); + s(McSecCarveoutSizeMb, 11:0, secure_scratch54, 11:0); + s(McMtsCarveoutBom, 31:20, secure_scratch54, 23:12); + s(McVideoProtectVprOverride1, 15:8, secure_scratch54, 31:24); + s(McMtsCarveoutSizeMb, 11:0, secure_scratch55, 11:0); + s(McGeneralizedCarveout4Size128kb, 11:0, secure_scratch55, 23:12); + s(McVideoProtectVprOverride1, 16:16, secure_scratch55, 24:24); + s(McGeneralizedCarveout2Cfg0, 2:0, secure_scratch55, 27:25); + s(McGeneralizedCarveout2Cfg0, 25:22, secure_scratch55, 31:28); + s(McGeneralizedCarveout3Size128kb, 11:0, secure_scratch56, 11:0); + s(McGeneralizedCarveout2Size128kb, 11:0, secure_scratch56, 23:12); + s(McGeneralizedCarveout2Cfg0, 26:26, secure_scratch56, 24:24); + s(McGeneralizedCarveout1Cfg0, 2:0, secure_scratch56, 27:25); + s(McGeneralizedCarveout1Cfg0, 25:22, secure_scratch56, 31:28); + s(McGeneralizedCarveout1Size128kb, 11:0, secure_scratch57, 11:0); + s(McGeneralizedCarveout5Size128kb, 11:0, secure_scratch57, 23:12); + s(McGeneralizedCarveout1Cfg0, 26:26, secure_scratch57, 24:24); + s(McGeneralizedCarveout3Cfg0, 2:0, secure_scratch57, 27:25); + s(McGeneralizedCarveout3Cfg0, 25:22, secure_scratch57, 31:28); + s(McGeneralizedCarveout3Cfg0, 26:26, secure_scratch58, 0:0); + + s32(McGeneralizedCarveout1Access0, secure_scratch59); + s32(McGeneralizedCarveout1Access1, secure_scratch60); + s32(McGeneralizedCarveout1Access2, secure_scratch61); + s32(McGeneralizedCarveout1Access3, secure_scratch62); + s32(McGeneralizedCarveout1Access4, secure_scratch63); + s32(McGeneralizedCarveout2Access0, secure_scratch64); + s32(McGeneralizedCarveout2Access1, secure_scratch65); + s32(McGeneralizedCarveout2Access2, secure_scratch66); + s32(McGeneralizedCarveout2Access3, secure_scratch67); + s32(McGeneralizedCarveout2Access4, secure_scratch68); + s32(McGeneralizedCarveout3Access0, secure_scratch69); + s32(McGeneralizedCarveout3Access1, secure_scratch70); + s32(McGeneralizedCarveout3Access2, secure_scratch71); + s32(McGeneralizedCarveout3Access3, secure_scratch72); + s32(McGeneralizedCarveout3Access4, secure_scratch73); + s32(McGeneralizedCarveout4Access0, secure_scratch74); + s32(McGeneralizedCarveout4Access1, secure_scratch75); + s32(McGeneralizedCarveout4Access2, secure_scratch76); + s32(McGeneralizedCarveout4Access3, secure_scratch77); + s32(McGeneralizedCarveout4Access4, secure_scratch78); + s32(McGeneralizedCarveout5Access0, secure_scratch79); + s32(McGeneralizedCarveout5Access1, secure_scratch80); + s32(McGeneralizedCarveout5Access2, secure_scratch81); + s32(McGeneralizedCarveout5Access3, secure_scratch82); + s32(McGeneralizedCarveout1ForceInternalAccess0, secure_scratch84); + s32(McGeneralizedCarveout1ForceInternalAccess1, secure_scratch85); + s32(McGeneralizedCarveout1ForceInternalAccess2, secure_scratch86); + s32(McGeneralizedCarveout1ForceInternalAccess3, secure_scratch87); + s32(McGeneralizedCarveout1ForceInternalAccess4, secure_scratch88); + s32(McGeneralizedCarveout2ForceInternalAccess0, secure_scratch89); + s32(McGeneralizedCarveout2ForceInternalAccess1, secure_scratch90); + s32(McGeneralizedCarveout2ForceInternalAccess2, secure_scratch91); + s32(McGeneralizedCarveout2ForceInternalAccess3, secure_scratch92); + s32(McGeneralizedCarveout2ForceInternalAccess4, secure_scratch93); + s32(McGeneralizedCarveout3ForceInternalAccess0, secure_scratch94); + s32(McGeneralizedCarveout3ForceInternalAccess1, secure_scratch95); + s32(McGeneralizedCarveout3ForceInternalAccess2, secure_scratch96); + s32(McGeneralizedCarveout3ForceInternalAccess3, secure_scratch97); + s32(McGeneralizedCarveout3ForceInternalAccess4, secure_scratch98); + s32(McGeneralizedCarveout4ForceInternalAccess0, secure_scratch99); + s32(McGeneralizedCarveout4ForceInternalAccess1, secure_scratch100); + s32(McGeneralizedCarveout4ForceInternalAccess2, secure_scratch101); + s32(McGeneralizedCarveout4ForceInternalAccess3, secure_scratch102); + s32(McGeneralizedCarveout4ForceInternalAccess4, secure_scratch103); + s32(McGeneralizedCarveout5ForceInternalAccess0, secure_scratch104); + s32(McGeneralizedCarveout5ForceInternalAccess1, secure_scratch105); + s32(McGeneralizedCarveout5ForceInternalAccess2, secure_scratch106); + s32(McGeneralizedCarveout5ForceInternalAccess3, secure_scratch107); + + c32(0, scratch2); + s(PllMInputDivider, 7:0, scratch2, 7:0); + s(PllMFeedbackDivider, 7:0, scratch2, 15:8); + s(PllMPostDivider, 4:0, scratch2, 20:16); + s(PllMKVCO, 0:0, scratch2, 21:21); + s(PllMKCP, 1:0, scratch2, 23:22); + + c32(0, scratch35); + s(PllMSetupControl, 15:0, scratch35, 15:0); + + c32(0, scratch3); + s(PllMInputDivider, 7:0, scratch3, 7:0); + c(0x3e, scratch3, 15:8); + c(0, scratch3, 20:16); + s(PllMKVCO, 0:0, scratch3, 21:21); + s(PllMKCP, 1:0, scratch3, 23:22); + + c32(0, scratch36); + s(PllMSetupControl, 23:0, scratch36, 23:0); + + c32(0, scratch4); + s(PllMStableTime, 9:0, scratch4, 9:0); +} \ No newline at end of file diff --git a/src/hwinit/sdram_lz.inl b/src/hwinit/sdram_lz.inl new file mode 100644 index 0000000..0974245 --- /dev/null +++ b/src/hwinit/sdram_lz.inl @@ -0,0 +1,124 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +static const u8 _dram_cfg_lz[1262] = { + 0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, + 0x00, 0x2C, 0x17, 0x04, 0x09, 0x00, 0x17, 0x04, 0x04, 0x17, 0x08, 0x08, + 0x17, 0x10, 0x10, 0x00, 0x00, 0x68, 0xBC, 0x01, 0x70, 0x0A, 0x00, 0x00, + 0x00, 0x04, 0xB4, 0x01, 0x70, 0x01, 0x32, 0x54, 0x76, 0xC8, 0xE6, 0x00, + 0x70, 0x17, 0x10, 0x24, 0x34, 0x00, 0x00, 0x00, 0x02, 0x80, 0x18, 0x40, + 0x00, 0x00, 0x00, 0x17, 0x04, 0x04, 0x17, 0x09, 0x18, 0xFF, 0xFF, 0x1F, + 0x00, 0xD8, 0x51, 0x1A, 0xA0, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x77, + 0x00, 0x17, 0x04, 0x04, 0x17, 0x08, 0x08, 0x17, 0x08, 0x08, 0xA6, 0xA6, + 0xAF, 0xB3, 0x3C, 0x9E, 0x00, 0x00, 0x03, 0x03, 0xE0, 0xC1, 0x04, 0x04, + 0x04, 0x04, 0x17, 0x04, 0x04, 0x17, 0x04, 0x3C, 0x1F, 0x1F, 0x1F, 0x1F, + 0x17, 0x04, 0x04, 0x17, 0x06, 0x06, 0x00, 0x00, 0x04, 0x08, 0x17, 0x06, + 0x46, 0xA1, 0x01, 0x00, 0x00, 0x32, 0x17, 0x0B, 0x64, 0x01, 0x17, 0x04, + 0x7C, 0x17, 0x07, 0x0C, 0x03, 0x17, 0x04, 0x04, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x13, + 0x17, 0x0B, 0x2C, 0x09, 0x00, 0x00, 0x00, 0x17, 0x05, 0x5D, 0x17, 0x07, + 0x10, 0x0B, 0x17, 0x07, 0x28, 0x08, 0x17, 0x07, 0x0C, 0x17, 0x04, 0x1C, + 0x20, 0x00, 0x00, 0x00, 0x06, 0x17, 0x04, 0x04, 0x17, 0x07, 0x08, 0x17, + 0x04, 0x50, 0x17, 0x04, 0x2C, 0x17, 0x04, 0x1C, 0x17, 0x04, 0x10, 0x17, + 0x08, 0x6C, 0x17, 0x04, 0x10, 0x17, 0x04, 0x38, 0x17, 0x04, 0x40, 0x05, + 0x17, 0x07, 0x1C, 0x17, 0x08, 0x58, 0x17, 0x04, 0x24, 0x17, 0x04, 0x18, + 0x17, 0x08, 0x64, 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x17, 0x09, 0x0C, 0x17, 0x05, 0x82, + 0x58, 0x17, 0x07, 0x61, 0xC1, 0x17, 0x07, 0x50, 0x17, 0x04, 0x04, 0x17, + 0x08, 0x81, 0x48, 0x17, 0x04, 0x04, 0x17, 0x04, 0x28, 0x17, 0x04, 0x60, + 0x17, 0x08, 0x54, 0x27, 0x17, 0x04, 0x04, 0x17, 0x07, 0x14, 0x17, 0x04, + 0x04, 0x04, 0x17, 0x07, 0x81, 0x58, 0x17, 0x0C, 0x0C, 0x1C, 0x03, 0x00, + 0x00, 0x0D, 0xA0, 0x60, 0x91, 0xBF, 0x3B, 0x17, 0x04, 0x5A, 0xF3, 0x0C, + 0x04, 0x05, 0x1B, 0x06, 0x02, 0x03, 0x07, 0x1C, 0x23, 0x25, 0x25, 0x05, + 0x08, 0x1D, 0x09, 0x0A, 0x24, 0x0B, 0x1E, 0x0D, 0x0C, 0x26, 0x26, 0x03, + 0x02, 0x1B, 0x1C, 0x23, 0x03, 0x04, 0x07, 0x05, 0x06, 0x25, 0x25, 0x02, + 0x0A, 0x0B, 0x1D, 0x0D, 0x08, 0x0C, 0x09, 0x1E, 0x24, 0x26, 0x26, 0x08, + 0x24, 0x06, 0x07, 0x9A, 0x12, 0x17, 0x05, 0x83, 0x41, 0x00, 0xFF, 0x17, + 0x10, 0x83, 0x6C, 0x04, 0x00, 0x01, 0x08, 0x00, 0x00, 0x02, 0x08, 0x00, + 0x00, 0x0D, 0x08, 0x00, 0x00, 0x00, 0xC0, 0x71, 0x71, 0x03, 0x08, 0x00, + 0x00, 0x0B, 0x08, 0x72, 0x72, 0x0E, 0x0C, 0x17, 0x04, 0x20, 0x08, 0x08, + 0x0D, 0x0C, 0x00, 0x00, 0x0D, 0x0C, 0x14, 0x14, 0x16, 0x08, 0x17, 0x06, + 0x2C, 0x11, 0x08, 0x17, 0x10, 0x84, 0x67, 0x15, 0x00, 0xCC, 0x00, 0x0A, + 0x00, 0x33, 0x00, 0x00, 0x00, 0x20, 0xF3, 0x05, 0x08, 0x11, 0x00, 0xFF, + 0x0F, 0xFF, 0x0F, 0x17, 0x08, 0x83, 0x4C, 0x01, 0x03, 0x00, 0x70, 0x00, + 0x0C, 0x00, 0x01, 0x17, 0x04, 0x0C, 0x08, 0x44, 0x00, 0x10, 0x04, 0x04, + 0x00, 0x06, 0x13, 0x07, 0x00, 0x80, 0x17, 0x04, 0x10, 0xA0, 0x00, 0x2C, + 0x00, 0x01, 0x37, 0x00, 0x00, 0x00, 0x80, 0x17, 0x06, 0x48, 0x08, 0x00, + 0x04, 0x00, 0x1F, 0x22, 0x20, 0x80, 0x0F, 0xF4, 0x20, 0x02, 0x28, 0x28, + 0x28, 0x28, 0x17, 0x04, 0x04, 0x11, 0x11, 0x11, 0x11, 0x17, 0x04, 0x04, + 0xBE, 0x00, 0x00, 0x17, 0x05, 0x58, 0x17, 0x08, 0x5C, 0x17, 0x22, 0x85, + 0x6A, 0x17, 0x1A, 0x1A, 0x14, 0x00, 0x12, 0x00, 0x10, 0x17, 0x05, 0x83, + 0x0A, 0x17, 0x16, 0x18, 0x30, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x30, 0x00, + 0x33, 0x00, 0x35, 0x00, 0x30, 0x00, 0x32, 0x17, 0x05, 0x83, 0x0C, 0x17, + 0x04, 0x20, 0x17, 0x18, 0x18, 0x28, 0x00, 0x28, 0x17, 0x04, 0x04, 0x17, + 0x08, 0x08, 0x17, 0x10, 0x10, 0x00, 0x14, 0x17, 0x05, 0x5A, 0x17, 0x04, + 0x5C, 0x17, 0x04, 0x5E, 0x17, 0x04, 0x0E, 0x17, 0x0E, 0x78, 0x17, 0x09, + 0x82, 0x50, 0x40, 0x06, 0x00, 0xCC, 0x00, 0x09, 0x00, 0x4F, 0x00, 0x51, + 0x17, 0x08, 0x18, 0x80, 0x01, 0x00, 0x00, 0x40, 0x17, 0x04, 0x20, 0x03, + 0x00, 0x00, 0x00, 0xAB, 0x00, 0x0A, 0x04, 0x11, 0x17, 0x08, 0x82, 0x58, + 0x17, 0x0C, 0x38, 0x17, 0x1B, 0x81, 0x6C, 0x17, 0x08, 0x85, 0x60, 0x17, + 0x08, 0x86, 0x50, 0x17, 0x08, 0x86, 0x60, 0x17, 0x06, 0x83, 0x21, 0x22, + 0x04, 0xFF, 0xFF, 0xAF, 0x4F, 0x17, 0x0C, 0x86, 0x74, 0x17, 0x08, 0x2C, + 0x8B, 0xFF, 0x07, 0x17, 0x06, 0x81, 0x04, 0x32, 0x54, 0x76, 0x10, 0x47, + 0x32, 0x65, 0x10, 0x34, 0x76, 0x25, 0x01, 0x34, 0x67, 0x25, 0x01, 0x75, + 0x64, 0x32, 0x01, 0x72, 0x56, 0x34, 0x10, 0x23, 0x74, 0x56, 0x01, 0x45, + 0x32, 0x67, 0x17, 0x04, 0x24, 0x49, 0x92, 0x24, 0x17, 0x04, 0x04, 0x17, + 0x11, 0x7C, 0x1B, 0x17, 0x04, 0x04, 0x17, 0x13, 0x81, 0x14, 0x2F, 0x41, + 0x13, 0x1F, 0x14, 0x00, 0x01, 0x00, 0x17, 0x04, 0x7C, 0xFF, 0xFF, 0xFF, + 0x7F, 0x0B, 0xD7, 0x06, 0x40, 0x00, 0x00, 0x02, 0x00, 0x08, 0x08, 0x03, + 0x00, 0x00, 0x5C, 0x01, 0x00, 0x10, 0x10, 0x10, 0x17, 0x06, 0x86, 0x59, + 0x17, 0x0F, 0x89, 0x14, 0x37, 0x17, 0x07, 0x82, 0x72, 0x10, 0x17, 0x06, + 0x83, 0x0D, 0x00, 0x11, 0x01, 0x17, 0x05, 0x85, 0x39, 0x17, 0x04, 0x0E, + 0x0A, 0x17, 0x07, 0x89, 0x29, 0x17, 0x04, 0x1B, 0x17, 0x08, 0x86, 0x77, + 0x17, 0x09, 0x12, 0x20, 0x00, 0x00, 0x00, 0x81, 0x10, 0x09, 0x28, 0x93, + 0x32, 0xA5, 0x44, 0x5B, 0x8A, 0x67, 0x76, 0x17, 0x18, 0x82, 0x2C, 0xFF, + 0xEF, 0xFF, 0xEF, 0xC0, 0xC0, 0xC0, 0xC0, 0x17, 0x04, 0x04, 0xDC, 0xDC, + 0xDC, 0xDC, 0x0A, 0x0A, 0x0A, 0x0A, 0x17, 0x04, 0x04, 0x17, 0x04, 0x04, + 0x17, 0x05, 0x82, 0x24, 0x03, 0x07, 0x17, 0x04, 0x04, 0x00, 0x00, 0x24, + 0xFF, 0xFF, 0x00, 0x44, 0x57, 0x6E, 0x00, 0x28, 0x72, 0x39, 0x00, 0x10, + 0x9C, 0x4B, 0x17, 0x04, 0x64, 0x01, 0x00, 0x00, 0x08, 0x4C, 0x00, 0x00, + 0x80, 0x20, 0x10, 0x0A, 0x00, 0x28, 0x10, 0x17, 0x06, 0x85, 0x60, 0x17, + 0x10, 0x82, 0x74, 0x17, 0x08, 0x08, 0x17, 0x08, 0x88, 0x00, 0x17, 0x04, + 0x10, 0x04, 0x17, 0x0B, 0x87, 0x6C, 0x01, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x03, 0x00, 0x04, 0x05, 0xC3, 0x71, 0x0F, 0x0F, 0x17, 0x08, 0x8B, 0x18, + 0x1F, 0x17, 0x09, 0x81, 0x73, 0x00, 0xFF, 0x00, 0xFF, 0x17, 0x05, 0x86, + 0x48, 0x17, 0x04, 0x0C, 0x17, 0x07, 0x86, 0x34, 0x00, 0x00, 0xF0, 0x17, + 0x09, 0x87, 0x54, 0x43, 0xC3, 0xBA, 0xE4, 0xD3, 0x1E, 0x17, 0x0C, 0x81, + 0x52, 0x17, 0x0A, 0x1C, 0x17, 0x10, 0x81, 0x6C, 0x17, 0x0A, 0x82, 0x21, + 0x17, 0x07, 0x82, 0x4D, 0x17, 0x0A, 0x8A, 0x1B, 0x17, 0x11, 0x2C, 0x76, + 0x0C, 0x17, 0x0A, 0x8A, 0x67, 0x17, 0x0F, 0x84, 0x28, 0x17, 0x06, 0x34, + 0x17, 0x17, 0x3A, 0x7E, 0x16, 0x40, 0x17, 0x0C, 0x8B, 0x1F, 0x17, 0x2A, + 0x38, 0x1E, 0x17, 0x0A, 0x38, 0x17, 0x13, 0x81, 0x28, 0x00, 0xC0, 0x17, + 0x17, 0x55, 0x46, 0x24, 0x17, 0x0A, 0x81, 0x28, 0x17, 0x14, 0x38, 0x17, + 0x18, 0x81, 0x60, 0x46, 0x2C, 0x17, 0x06, 0x38, 0xEC, 0x17, 0x0D, 0x16, + 0x17, 0x0E, 0x82, 0x3C, 0x17, 0x82, 0x0C, 0x8E, 0x68, 0x17, 0x04, 0x24, + 0x17, 0x5C, 0x8E, 0x68, 0x17, 0x07, 0x82, 0x5F, 0x80, 0x17, 0x87, 0x01, + 0x8E, 0x68, 0x02, 0x17, 0x81, 0x4A, 0x8E, 0x68, 0x17, 0x0C, 0x87, 0x78, + 0x17, 0x85, 0x28, 0x8E, 0x68, 0x17, 0x8E, 0x68, 0x9D, 0x50, 0x17, 0x81, + 0x24, 0x8E, 0x68, 0x17, 0x04, 0x2C, 0x17, 0x28, 0x8E, 0x68, 0x17, 0x04, + 0x30, 0x17, 0x85, 0x3C, 0x8E, 0x68, 0x12, 0x17, 0x07, 0x85, 0x70, 0x17, + 0x88, 0x74, 0x8E, 0x68, 0x17, 0x87, 0x3E, 0x9D, 0x50, 0x0C, 0x17, 0x04, + 0x04, 0x17, 0x12, 0x8E, 0x68, 0x18, 0x17, 0x87, 0x12, 0xBB, 0x20, 0x17, + 0x83, 0x04, 0x9D, 0x50, 0x15, 0x17, 0x05, 0x8D, 0x76, 0x17, 0x0F, 0x8B, + 0x49, 0x17, 0x0B, 0x18, 0x32, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, + 0x34, 0x00, 0x36, 0x00, 0x2F, 0x00, 0x33, 0x17, 0x09, 0x84, 0x0C, 0x17, + 0x18, 0x18, 0x17, 0x20, 0x8E, 0x68, 0x15, 0x17, 0x07, 0x5A, 0x17, 0x06, + 0x5E, 0x16, 0x00, 0x15, 0x17, 0x82, 0x40, 0x9D, 0x50, 0x17, 0x86, 0x5F, + 0xBB, 0x20, 0x3A, 0x00, 0x00, 0x00, 0x1D, 0x17, 0x81, 0x4F, 0xAC, 0x38, + 0x3B, 0x17, 0x04, 0x04, 0x17, 0x86, 0x30, 0x8E, 0x68, 0x17, 0x81, 0x53, + 0xAC, 0x38, 0x07, 0x17, 0x0D, 0x8E, 0x68, 0xA3, 0x72, 0x17, 0x83, 0x10, + 0x8E, 0x68 +}; diff --git a/src/hwinit/sdram_param_t210.h b/src/hwinit/sdram_param_t210.h new file mode 100644 index 0000000..2ab6d42 --- /dev/null +++ b/src/hwinit/sdram_param_t210.h @@ -0,0 +1,930 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * + * See file CREDITS for list of people who contributed to this + * project. + */ + +/** + * Defines the SDRAM parameter structure. + * + * Note that PLLM is used by EMC. + */ + +#ifndef _SDRAM_PARAM_T210_H_ +#define _SDRAM_PARAM_T210_H_ + +#define MEMORY_TYPE_NONE 0 +#define MEMORY_TYPE_DDR 0 +#define MEMORY_TYPE_LPDDR 0 +#define MEMORY_TYPE_DDR2 0 +#define MEMORY_TYPE_LPDDR2 1 +#define MEMORY_TYPE_DDR3 2 +#define MEMORY_TYPE_LPDDR4 3 + +/** + * Defines the SDRAM parameter structure + */ +typedef struct _sdram_params { + /* Specifies the type of memory device */ + u32 memory_type; + + /* MC/EMC clock source configuration */ + + /* Specifies the M value for PllM */ + u32 pllm_input_divider; + /* Specifies the N value for PllM */ + u32 pllm_feedback_divider; + /* Specifies the time to wait for PLLM to lock (in microseconds) */ + u32 pllm_stable_time; + /* Specifies misc. control bits */ + u32 pllm_setup_control; + /* Specifies the P value for PLLM */ + u32 pllm_post_divider; + /* Specifies value for Charge Pump Gain Control */ + u32 pllm_kcp; + /* Specifies VCO gain */ + u32 pllm_kvco; + /* Spare BCT param */ + u32 emc_bct_spare0; + /* Spare BCT param */ + u32 emc_bct_spare1; + /* Spare BCT param */ + u32 emc_bct_spare2; + /* Spare BCT param */ + u32 emc_bct_spare3; + /* Spare BCT param */ + u32 emc_bct_spare4; + /* Spare BCT param */ + u32 emc_bct_spare5; + /* Spare BCT param */ + u32 emc_bct_spare6; + /* Spare BCT param */ + u32 emc_bct_spare7; + /* Spare BCT param */ + u32 emc_bct_spare8; + /* Spare BCT param */ + u32 emc_bct_spare9; + /* Spare BCT param */ + u32 emc_bct_spare10; + /* Spare BCT param */ + u32 emc_bct_spare11; + /* Spare BCT param */ + u32 emc_bct_spare12; + /* Spare BCT param */ + u32 emc_bct_spare13; + + /* Defines EMC_2X_CLK_SRC, EMC_2X_CLK_DIVISOR, EMC_INVERT_DCD */ + u32 emc_clock_source; + u32 emc_clock_source_dll; + + /* Defines possible override for PLLLM_MISC2 */ + u32 clk_rst_pllm_misc20_override; + /* enables override for PLLLM_MISC2 */ + u32 clk_rst_pllm_misc20_override_enable; + /* defines CLK_ENB_MC1 in register clk_rst_controller_clk_enb_w_clr */ + u32 clear_clock2_mc1; + + /* Auto-calibration of EMC pads */ + + /* Specifies the value for EMC_AUTO_CAL_INTERVAL */ + u32 emc_auto_cal_interval; + /* + * Specifies the value for EMC_AUTO_CAL_CONFIG + * Note: Trigger bits are set by the SDRAM code. + */ + u32 emc_auto_cal_config; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG2 */ + u32 emc_auto_cal_config2; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG3 */ + u32 emc_auto_cal_config3; + + u32 emc_auto_cal_config4; + u32 emc_auto_cal_config5; + u32 emc_auto_cal_config6; + u32 emc_auto_cal_config7; + u32 emc_auto_cal_config8; + /* Specifies the value for EMC_AUTO_CAL_VREF_SEL_0 */ + u32 emc_auto_cal_vref_sel0; + u32 emc_auto_cal_vref_sel1; + + /* Specifies the value for EMC_AUTO_CAL_CHANNEL */ + u32 emc_auto_cal_channel; + + /* Specifies the value for EMC_PMACRO_AUTOCAL_CFG_0 */ + u32 emc_pmacro_auto_cal_cfg0; + u32 emc_pmacro_auto_cal_cfg1; + u32 emc_pmacro_auto_cal_cfg2; + + u32 emc_pmacro_rx_term; + u32 emc_pmacro_dq_tx_drive; + u32 emc_pmacro_ca_tx_drive; + u32 emc_pmacro_cmd_tx_drive; + u32 emc_pmacro_auto_cal_common; + u32 emc_pmacro_zcrtl; + + /* + * Specifies the time for the calibration + * to stabilize (in microseconds) + */ + u32 emc_auto_cal_wait; + + u32 emc_xm2_comp_pad_ctrl; + u32 emc_xm2_comp_pad_ctrl2; + u32 emc_xm2_comp_pad_ctrl3; + + /* + * DRAM size information + * Specifies the value for EMC_ADR_CFG + */ + u32 emc_adr_cfg; + + /* + * Specifies the time to wait after asserting pin + * CKE (in microseconds) + */ + u32 emc_pin_program_wait; + /* Specifies the extra delay before/after pin RESET/CKE command */ + u32 emc_pin_extra_wait; + + u32 emc_pin_gpio_enable; + u32 emc_pin_gpio; + + /* + * Specifies the extra delay after the first writing + * of EMC_TIMING_CONTROL + */ + u32 emc_timing_control_wait; + + /* Timing parameters required for the SDRAM */ + + /* Specifies the value for EMC_RC */ + u32 emc_rc; + /* Specifies the value for EMC_RFC */ + u32 emc_rfc; + + u32 emc_rfc_pb; + u32 emc_ref_ctrl2; + + /* Specifies the value for EMC_RFC_SLR */ + u32 emc_rfc_slr; + /* Specifies the value for EMC_RAS */ + u32 emc_ras; + /* Specifies the value for EMC_RP */ + u32 emc_rp; + /* Specifies the value for EMC_R2R */ + u32 emc_r2r; + /* Specifies the value for EMC_W2W */ + u32 emc_w2w; + /* Specifies the value for EMC_R2W */ + u32 emc_r2w; + /* Specifies the value for EMC_W2R */ + u32 emc_w2r; + /* Specifies the value for EMC_R2P */ + u32 emc_r2p; + /* Specifies the value for EMC_W2P */ + u32 emc_w2p; + /* Specifies the value for EMC_RD_RCD */ + + u32 emc_tppd; + u32 emc_ccdmw; + + u32 emc_rd_rcd; + /* Specifies the value for EMC_WR_RCD */ + u32 emc_wr_rcd; + /* Specifies the value for EMC_RRD */ + u32 emc_rrd; + /* Specifies the value for EMC_REXT */ + u32 emc_rext; + /* Specifies the value for EMC_WEXT */ + u32 emc_wext; + /* Specifies the value for EMC_WDV */ + u32 emc_wdv; + + u32 emc_wdv_chk; + u32 emc_wsv; + u32 emc_wev; + + /* Specifies the value for EMC_WDV_MASK */ + u32 emc_wdv_mask; + + u32 emc_ws_duration; + u32 emc_we_duration; + + /* Specifies the value for EMC_QUSE */ + u32 emc_quse; + /* Specifies the value for EMC_QUSE_WIDTH */ + u32 emc_quse_width; + /* Specifies the value for EMC_IBDLY */ + u32 emc_ibdly; + + u32 emc_obdly; + + /* Specifies the value for EMC_EINPUT */ + u32 emc_einput; + /* Specifies the value for EMC_EINPUT_DURATION */ + u32 emc_einput_duration; + /* Specifies the value for EMC_PUTERM_EXTRA */ + u32 emc_puterm_extra; + /* Specifies the value for EMC_PUTERM_WIDTH */ + u32 emc_puterm_width; + + u32 emc_qrst; + u32 emc_qsafe; + u32 emc_rdv; + u32 emc_rdv_mask; + + u32 emc_rdv_early; + u32 emc_rdv_early_mask; + + /* Specifies the value for EMC_QPOP */ + u32 emc_qpop; + + /* Specifies the value for EMC_REFRESH */ + u32 emc_refresh; + /* Specifies the value for EMC_BURST_REFRESH_NUM */ + u32 emc_burst_refresh_num; + /* Specifies the value for EMC_PRE_REFRESH_REQ_CNT */ + u32 emc_prerefresh_req_cnt; + /* Specifies the value for EMC_PDEX2WR */ + u32 emc_pdex2wr; + /* Specifies the value for EMC_PDEX2RD */ + u32 emc_pdex2rd; + /* Specifies the value for EMC_PCHG2PDEN */ + u32 emc_pchg2pden; + /* Specifies the value for EMC_ACT2PDEN */ + u32 emc_act2pden; + /* Specifies the value for EMC_AR2PDEN */ + u32 emc_ar2pden; + /* Specifies the value for EMC_RW2PDEN */ + u32 emc_rw2pden; + + u32 emc_cke2pden; + u32 emc_pdex2che; + u32 emc_pdex2mrr; + + /* Specifies the value for EMC_TXSR */ + u32 emc_txsr; + /* Specifies the value for EMC_TXSRDLL */ + u32 emc_txsr_dll; + /* Specifies the value for EMC_TCKE */ + u32 emc_tcke; + /* Specifies the value for EMC_TCKESR */ + u32 emc_tckesr; + /* Specifies the value for EMC_TPD */ + u32 emc_tpd; + /* Specifies the value for EMC_TFAW */ + u32 emc_tfaw; + /* Specifies the value for EMC_TRPAB */ + u32 emc_trpab; + /* Specifies the value for EMC_TCLKSTABLE */ + u32 emc_tclkstable; + /* Specifies the value for EMC_TCLKSTOP */ + u32 emc_tclkstop; + /* Specifies the value for EMC_TREFBW */ + u32 emc_trefbw; + + /* FBIO configuration values */ + + /* Specifies the value for EMC_FBIO_CFG5 */ + u32 emc_fbio_cfg5; + /* Specifies the value for EMC_FBIO_CFG7 */ + u32 emc_fbio_cfg7; + u32 emc_fbio_cfg8; + + /* Command mapping for CMD brick 0 */ + u32 emc_cmd_mapping_cmd0_0; + u32 emc_cmd_mapping_cmd0_1; + u32 emc_cmd_mapping_cmd0_2; + u32 emc_cmd_mapping_cmd1_0; + u32 emc_cmd_mapping_cmd1_1; + u32 emc_cmd_mapping_cmd1_2; + u32 emc_cmd_mapping_cmd2_0; + u32 emc_cmd_mapping_cmd2_1; + u32 emc_cmd_mapping_cmd2_2; + u32 emc_cmd_mapping_cmd3_0; + u32 emc_cmd_mapping_cmd3_1; + u32 emc_cmd_mapping_cmd3_2; + u32 emc_cmd_mapping_byte; + + /* Specifies the value for EMC_FBIO_SPARE */ + u32 emc_fbio_spare; + + /* Specifies the value for EMC_CFG_RSV */ + u32 emc_cfg_rsv; + + /* MRS command values */ + + /* Specifies the value for EMC_MRS */ + u32 emc_mrs; + /* Specifies the MP0 command to initialize mode registers */ + u32 emc_emrs; + /* Specifies the MP2 command to initialize mode registers */ + u32 emc_emrs2; + /* Specifies the MP3 command to initialize mode registers */ + u32 emc_emrs3; + /* Specifies the programming to LPDDR2 Mode Register 1 at cold boot */ + u32 emc_mrw1; + /* Specifies the programming to LPDDR2 Mode Register 2 at cold boot */ + u32 emc_mrw2; + /* Specifies the programming to LPDDR2 Mode Register 3 at cold boot */ + u32 emc_mrw3; + /* Specifies the programming to LPDDR2 Mode Register 11 at cold boot */ + u32 emc_mrw4; + + /* Specifies the programming to LPDDR4 Mode Register 3 at cold boot */ + u32 emc_mrw6; + /* Specifies the programming to LPDDR4 Mode Register 11 at cold boot */ + u32 emc_mrw8; + /* Specifies the programming to LPDDR4 Mode Register 11 at cold boot */ + u32 emc_mrw9; + /* Specifies the programming to LPDDR4 Mode Register 12 at cold boot */ + u32 emc_mrw10; + /* Specifies the programming to LPDDR4 Mode Register 14 at cold boot */ + u32 emc_mrw12; + /* Specifies the programming to LPDDR4 Mode Register 14 at cold boot */ + u32 emc_mrw13; + /* Specifies the programming to LPDDR4 Mode Register 22 at cold boot */ + u32 emc_mrw14; + + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at cold boot + */ + u32 emc_mrw_extra; + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at warm boot + */ + u32 emc_warm_boot_mrw_extra; + /* + * Specify the enable of extra Mode Register programming at + * warm boot + */ + u32 emc_warm_boot_extramode_reg_write_enable; + /* + * Specify the enable of extra Mode Register programming at + * cold boot + */ + u32 emc_extramode_reg_write_enable; + + /* Specifies the EMC_MRW reset command value */ + u32 emc_mrw_reset_command; + /* Specifies the EMC Reset wait time (in microseconds) */ + u32 emc_mrw_reset_ninit_wait; + /* Specifies the value for EMC_MRS_WAIT_CNT */ + u32 emc_mrs_wait_cnt; + /* Specifies the value for EMC_MRS_WAIT_CNT2 */ + u32 emc_mrs_wait_cnt2; + + /* EMC miscellaneous configurations */ + + /* Specifies the value for EMC_CFG */ + u32 emc_cfg; + /* Specifies the value for EMC_CFG_2 */ + u32 emc_cfg2; + /* Specifies the pipe bypass controls */ + u32 emc_cfg_pipe; + + u32 emc_cfg_pipe_clk; + u32 emc_fdpd_ctrl_cmd_no_ramp; + u32 emc_cfg_update; + + /* Specifies the value for EMC_DBG */ + u32 emc_dbg; + + u32 emc_dbg_write_mux; + + /* Specifies the value for EMC_CMDQ */ + u32 emc_cmd_q; + /* Specifies the value for EMC_MC2EMCQ */ + u32 emc_mc2emc_q; + /* Specifies the value for EMC_DYN_SELF_REF_CONTROL */ + u32 emc_dyn_self_ref_control; + + /* Specifies the value for MEM_INIT_DONE */ + u32 ahb_arbitration_xbar_ctrl_meminit_done; + + /* Specifies the value for EMC_CFG_DIG_DLL */ + u32 emc_cfg_dig_dll; + u32 emc_cfg_dig_dll_1; + + /* Specifies the value for EMC_CFG_DIG_DLL_PERIOD */ + u32 emc_cfg_dig_dll_period; + /* Specifies the value of *DEV_SELECTN of various EMC registers */ + u32 emc_dev_select; + + /* Specifies the value for EMC_SEL_DPD_CTRL */ + u32 emc_sel_dpd_ctrl; + + /* Pads trimmer delays */ + u32 emc_fdpd_ctrl_dq; + u32 emc_fdpd_ctrl_cmd; + u32 emc_pmacro_ib_vref_dq_0; + u32 emc_pmacro_ib_vref_dq_1; + u32 emc_pmacro_ib_vref_dqs_0; + u32 emc_pmacro_ib_vref_dqs_1; + u32 emc_pmacro_ib_rxrt; + u32 emc_cfg_pipe1; + u32 emc_cfg_pipe2; + + /* Specifies the value for EMC_PMACRO_QUSE_DDLL_RANK0_0 */ + u32 emc_pmacro_quse_ddll_rank0_0; + u32 emc_pmacro_quse_ddll_rank0_1; + u32 emc_pmacro_quse_ddll_rank0_2; + u32 emc_pmacro_quse_ddll_rank0_3; + u32 emc_pmacro_quse_ddll_rank0_4; + u32 emc_pmacro_quse_ddll_rank0_5; + u32 emc_pmacro_quse_ddll_rank1_0; + u32 emc_pmacro_quse_ddll_rank1_1; + u32 emc_pmacro_quse_ddll_rank1_2; + u32 emc_pmacro_quse_ddll_rank1_3; + u32 emc_pmacro_quse_ddll_rank1_4; + u32 emc_pmacro_quse_ddll_rank1_5; + + u32 emc_pmacro_ob_ddll_long_dq_rank0_0; + u32 emc_pmacro_ob_ddll_long_dq_rank0_1; + u32 emc_pmacro_ob_ddll_long_dq_rank0_2; + u32 emc_pmacro_ob_ddll_long_dq_rank0_3; + u32 emc_pmacro_ob_ddll_long_dq_rank0_4; + u32 emc_pmacro_ob_ddll_long_dq_rank0_5; + u32 emc_pmacro_ob_ddll_long_dq_rank1_0; + u32 emc_pmacro_ob_ddll_long_dq_rank1_1; + u32 emc_pmacro_ob_ddll_long_dq_rank1_2; + u32 emc_pmacro_ob_ddll_long_dq_rank1_3; + u32 emc_pmacro_ob_ddll_long_dq_rank1_4; + u32 emc_pmacro_ob_ddll_long_dq_rank1_5; + + u32 emc_pmacro_ob_ddll_long_dqs_rank0_0; + u32 emc_pmacro_ob_ddll_long_dqs_rank0_1; + u32 emc_pmacro_ob_ddll_long_dqs_rank0_2; + u32 emc_pmacro_ob_ddll_long_dqs_rank0_3; + u32 emc_pmacro_ob_ddll_long_dqs_rank0_4; + u32 emc_pmacro_ob_ddll_long_dqs_rank0_5; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_0; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_1; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_2; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_3; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_4; + u32 emc_pmacro_ob_ddll_long_dqs_rank1_5; + + u32 emc_pmacro_ib_ddll_long_dqs_rank0_0; + u32 emc_pmacro_ib_ddll_long_dqs_rank0_1; + u32 emc_pmacro_ib_ddll_long_dqs_rank0_2; + u32 emc_pmacro_ib_ddll_long_dqs_rank0_3; + u32 emc_pmacro_ib_ddll_long_dqs_rank1_0; + u32 emc_pmacro_ib_ddll_long_dqs_rank1_1; + u32 emc_pmacro_ib_ddll_long_dqs_rank1_2; + u32 emc_pmacro_ib_ddll_long_dqs_rank1_3; + + u32 emc_pmacro_ddll_long_cmd_0; + u32 emc_pmacro_ddll_long_cmd_1; + u32 emc_pmacro_ddll_long_cmd_2; + u32 emc_pmacro_ddll_long_cmd_3; + u32 emc_pmacro_ddll_long_cmd_4; + u32 emc_pmacro_ddll_short_cmd_0; + u32 emc_pmacro_ddll_short_cmd_1; + u32 emc_pmacro_ddll_short_cmd_2; + + /* + * Specifies the delay after asserting CKE pin during a WarmBoot0 + * sequence (in microseconds) + */ + u32 warm_boot_wait; + + /* Specifies the value for EMC_ODT_WRITE */ + u32 emc_odt_write; + + /* Periodic ZQ calibration */ + + /* + * Specifies the value for EMC_ZCAL_INTERVAL + * Value 0 disables ZQ calibration + */ + u32 emc_zcal_interval; + /* Specifies the value for EMC_ZCAL_WAIT_CNT */ + u32 emc_zcal_wait_cnt; + /* Specifies the value for EMC_ZCAL_MRW_CMD */ + u32 emc_zcal_mrw_cmd; + + /* DRAM initialization sequence flow control */ + + /* Specifies the MRS command value for resetting DLL */ + u32 emc_mrs_reset_dll; + /* Specifies the command for ZQ initialization of device 0 */ + u32 emc_zcal_init_dev0; + /* Specifies the command for ZQ initialization of device 1 */ + u32 emc_zcal_init_dev1; + /* + * Specifies the wait time after programming a ZQ initialization + * command (in microseconds) + */ + u32 emc_zcal_init_wait; + /* + * Specifies the enable for ZQ calibration at cold boot [bit 0] + * and warm boot [bit 1] + */ + u32 emc_zcal_warm_cold_boot_enables; + + /* + * Specifies the MRW command to LPDDR2 for ZQ calibration + * on warmboot + */ + /* Is issued to both devices separately */ + u32 emc_mrw_lpddr2zcal_warm_boot; + /* + * Specifies the ZQ command to DDR3 for ZQ calibration on warmboot + * Is issued to both devices separately + */ + u32 emc_zqcal_ddr3_warm_boot; + + u32 emc_zqcal_lpddr4_warm_boot; + + /* + * Specifies the wait time for ZQ calibration on warmboot + * (in microseconds) + */ + u32 emc_zcal_warm_boot_wait; + /* + * Specifies the enable for DRAM Mode Register programming + * at warm boot + */ + u32 emc_mrs_warm_boot_enable; + /* + * Specifies the wait time after sending an MRS DLL reset command + * in microseconds) + */ + u32 emc_mrs_reset_dll_wait; + /* Specifies the extra MRS command to initialize mode registers */ + u32 emc_mrs_extra; + /* Specifies the extra MRS command at warm boot */ + u32 emc_warm_boot_mrs_extra; + /* Specifies the EMRS command to enable the DDR2 DLL */ + u32 emc_emrs_ddr2_dll_enable; + /* Specifies the MRS command to reset the DDR2 DLL */ + u32 emc_mrs_ddr2_dll_reset; + /* Specifies the EMRS command to set OCD calibration */ + u32 emc_emrs_ddr2_ocd_calib; + /* + * Specifies the wait between initializing DDR and setting OCD + * calibration (in microseconds) + */ + u32 emc_ddr2_wait; + /* Specifies the value for EMC_CLKEN_OVERRIDE */ + u32 emc_clken_override; + /* + * Specifies LOG2 of the extra refresh numbers after booting + * Program 0 to disable + */ + u32 emc_extra_refresh_num; + /* Specifies the master override for all EMC clocks */ + u32 emc_clken_override_allwarm_boot; + /* Specifies the master override for all MC clocks */ + u32 mc_clken_override_allwarm_boot; + /* Specifies digital dll period, choosing between 4 to 64 ms */ + u32 emc_cfg_dig_dll_period_warm_boot; + + /* Pad controls */ + + /* Specifies the value for PMC_VDDP_SEL */ + u32 pmc_vddp_sel; + /* Specifies the wait time after programming PMC_VDDP_SEL */ + u32 pmc_vddp_sel_wait; + /* Specifies the value for PMC_DDR_PWR */ + u32 pmc_ddr_pwr; + /* Specifies the value for PMC_DDR_CFG */ + u32 pmc_ddr_cfg; + /* Specifies the value for PMC_IO_DPD3_REQ */ + u32 pmc_io_dpd3_req; + /* Specifies the wait time after programming PMC_IO_DPD3_REQ */ + u32 pmc_io_dpd3_req_wait; + + u32 pmc_io_dpd4_req_wait; + + /* Specifies the value for PMC_REG_SHORT */ + u32 pmc_reg_short; + /* Specifies the value for PMC_NO_IOPOWER */ + u32 pmc_no_io_power; + + u32 pmc_ddr_ctrl_wait; + u32 pmc_ddr_ctrl; + + /* Specifies the value for EMC_ACPD_CONTROL */ + u32 emc_acpd_control; + + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE0 */ + u32 emc_swizzle_rank0_byte0; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE1 */ + u32 emc_swizzle_rank0_byte1; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE2 */ + u32 emc_swizzle_rank0_byte2; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE3 */ + u32 emc_swizzle_rank0_byte3; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE0 */ + u32 emc_swizzle_rank1_byte0; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE1 */ + u32 emc_swizzle_rank1_byte1; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE2 */ + u32 emc_swizzle_rank1_byte2; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE3 */ + u32 emc_swizzle_rank1_byte3; + + /* Specifies the value for EMC_TXDSRVTTGEN */ + u32 emc_txdsrvttgen; + + /* Specifies the value for EMC_DATA_BRLSHFT_0 */ + u32 emc_data_brlshft0; + u32 emc_data_brlshft1; + + u32 emc_dqs_brlshft0; + u32 emc_dqs_brlshft1; + + u32 emc_cmd_brlshft0; + u32 emc_cmd_brlshft1; + u32 emc_cmd_brlshft2; + u32 emc_cmd_brlshft3; + + u32 emc_quse_brlshft0; + u32 emc_quse_brlshft1; + u32 emc_quse_brlshft2; + u32 emc_quse_brlshft3; + + u32 emc_dll_cfg0; + u32 emc_dll_cfg1; + + u32 emc_pmc_scratch1; + u32 emc_pmc_scratch2; + u32 emc_pmc_scratch3; + + u32 emc_pmacro_pad_cfg_ctrl; + + u32 emc_pmacro_vttgen_ctrl0; + u32 emc_pmacro_vttgen_ctrl1; + u32 emc_pmacro_vttgen_ctrl2; + + u32 emc_pmacro_brick_ctrl_rfu1; + u32 emc_pmacro_cmd_brick_ctrl_fdpd; + u32 emc_pmacro_brick_ctrl_rfu2; + u32 emc_pmacro_data_brick_ctrl_fdpd; + u32 emc_pmacro_bg_bias_ctrl0; + u32 emc_pmacro_data_pad_rx_ctrl; + u32 emc_pmacro_cmd_pad_rx_ctrl; + u32 emc_pmacro_data_rx_term_mode; + u32 emc_pmacro_cmd_rx_term_mode; + u32 emc_pmacro_data_pad_tx_ctrl; + u32 emc_pmacro_common_pad_tx_ctrl; + u32 emc_pmacro_cmd_pad_tx_ctrl; + u32 emc_cfg3; + + u32 emc_pmacro_tx_pwrd0; + u32 emc_pmacro_tx_pwrd1; + u32 emc_pmacro_tx_pwrd2; + u32 emc_pmacro_tx_pwrd3; + u32 emc_pmacro_tx_pwrd4; + u32 emc_pmacro_tx_pwrd5; + + u32 emc_config_sample_delay; + + u32 emc_pmacro_brick_mapping0; + u32 emc_pmacro_brick_mapping1; + u32 emc_pmacro_brick_mapping2; + + u32 emc_pmacro_tx_sel_clk_src0; + u32 emc_pmacro_tx_sel_clk_src1; + u32 emc_pmacro_tx_sel_clk_src2; + u32 emc_pmacro_tx_sel_clk_src3; + u32 emc_pmacro_tx_sel_clk_src4; + u32 emc_pmacro_tx_sel_clk_src5; + + u32 emc_pmacro_ddll_bypass; + + u32 emc_pmacro_ddll_pwrd0; + u32 emc_pmacro_ddll_pwrd1; + u32 emc_pmacro_ddll_pwrd2; + + u32 emc_pmacro_cmd_ctrl0; + u32 emc_pmacro_cmd_ctrl1; + u32 emc_pmacro_cmd_ctrl2; + + /* DRAM size information */ + + /* Specifies the value for MC_EMEM_ADR_CFG */ + u32 mc_emem_adr_cfg; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV0 */ + u32 mc_emem_adr_cfg_dev0; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV1 */ + u32 mc_emem_adr_cfg_dev1; + + u32 mc_emem_adr_cfg_channel_mask; + + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG0 */ + u32 mc_emem_adr_cfg_bank_mask0; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG1 */ + u32 mc_emem_adr_cfg_bank_mask1; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG2 */ + u32 mc_emem_adr_cfg_bank_mask2; + + /* + * Specifies the value for MC_EMEM_CFG which holds the external memory + * size (in KBytes) + */ + u32 mc_emem_cfg; + + /* MC arbitration configuration */ + + /* Specifies the value for MC_EMEM_ARB_CFG */ + u32 mc_emem_arb_cfg; + /* Specifies the value for MC_EMEM_ARB_OUTSTANDING_REQ */ + u32 mc_emem_arb_outstanding_req; + + u32 emc_emem_arb_refpb_hp_ctrl; + u32 emc_emem_arb_refpb_bank_ctrl; + + /* Specifies the value for MC_EMEM_ARB_TIMING_RCD */ + u32 mc_emem_arb_timing_rcd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RP */ + u32 mc_emem_arb_timing_rp; + /* Specifies the value for MC_EMEM_ARB_TIMING_RC */ + u32 mc_emem_arb_timing_rc; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAS */ + u32 mc_emem_arb_timing_ras; + /* Specifies the value for MC_EMEM_ARB_TIMING_FAW */ + u32 mc_emem_arb_timing_faw; + /* Specifies the value for MC_EMEM_ARB_TIMING_RRD */ + u32 mc_emem_arb_timing_rrd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAP2PRE */ + u32 mc_emem_arb_timing_rap2pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_WAP2PRE */ + u32 mc_emem_arb_timing_wap2pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2R */ + u32 mc_emem_arb_timing_r2r; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2W */ + u32 mc_emem_arb_timing_w2w; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2W */ + u32 mc_emem_arb_timing_r2w; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2R */ + u32 mc_emem_arb_timing_w2r; + + u32 mc_emem_arb_timing_rfcpb; + + /* Specifies the value for MC_EMEM_ARB_DA_TURNS */ + u32 mc_emem_arb_da_turns; + /* Specifies the value for MC_EMEM_ARB_DA_COVERS */ + u32 mc_emem_arb_da_covers; + /* Specifies the value for MC_EMEM_ARB_MISC0 */ + u32 mc_emem_arb_misc0; + /* Specifies the value for MC_EMEM_ARB_MISC1 */ + u32 mc_emem_arb_misc1; + u32 mc_emem_arb_misc2; + + /* Specifies the value for MC_EMEM_ARB_RING1_THROTTLE */ + u32 mc_emem_arb_ring1_throttle; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE */ + u32 mc_emem_arb_override; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE_1 */ + u32 mc_emem_arb_override1; + /* Specifies the value for MC_EMEM_ARB_RSV */ + u32 mc_emem_arb_rsv; + + u32 mc_da_cfg0; + u32 mc_emem_arb_timing_ccdmw; + + /* Specifies the value for MC_CLKEN_OVERRIDE */ + u32 mc_clken_override; + + /* Specifies the value for MC_STAT_CONTROL */ + u32 mc_stat_control; + /* Specifies the value for MC_VIDEO_PROTECT_BOM */ + u32 mc_video_protect_bom; + /* Specifies the value for MC_VIDEO_PROTECT_BOM_ADR_HI */ + u32 mc_video_protect_bom_adr_hi; + /* Specifies the value for MC_VIDEO_PROTECT_SIZE_MB */ + u32 mc_video_protect_size_mb; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE */ + u32 mc_video_protect_vpr_override; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE1 */ + u32 mc_video_protect_vpr_override1; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_0 */ + u32 mc_video_protect_gpu_override0; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_1 */ + u32 mc_video_protect_gpu_override1; + /* Specifies the value for MC_SEC_CARVEOUT_BOM */ + u32 mc_sec_carveout_bom; + /* Specifies the value for MC_SEC_CARVEOUT_ADR_HI */ + u32 mc_sec_carveout_adr_hi; + /* Specifies the value for MC_SEC_CARVEOUT_SIZE_MB */ + u32 mc_sec_carveout_size_mb; + /* Specifies the value for MC_VIDEO_PROTECT_REG_CTRL.VIDEO_PROTECT_WRITE_ACCESS */ + u32 mc_video_protect_write_access; + /* Specifies the value for MC_SEC_CARVEOUT_REG_CTRL.SEC_CARVEOUT_WRITE_ACCESS */ + u32 mc_sec_carveout_protect_write_access; + + u32 mc_generalized_carveout1_bom; + u32 mc_generalized_carveout1_bom_hi; + u32 mc_generalized_carveout1_size_128kb; + u32 mc_generalized_carveout1_access0; + u32 mc_generalized_carveout1_access1; + u32 mc_generalized_carveout1_access2; + u32 mc_generalized_carveout1_access3; + u32 mc_generalized_carveout1_access4; + u32 mc_generalized_carveout1_force_internal_access0; + u32 mc_generalized_carveout1_force_internal_access1; + u32 mc_generalized_carveout1_force_internal_access2; + u32 mc_generalized_carveout1_force_internal_access3; + u32 mc_generalized_carveout1_force_internal_access4; + u32 mc_generalized_carveout1_cfg0; + + u32 mc_generalized_carveout2_bom; + u32 mc_generalized_carveout2_bom_hi; + u32 mc_generalized_carveout2_size_128kb; + u32 mc_generalized_carveout2_access0; + u32 mc_generalized_carveout2_access1; + u32 mc_generalized_carveout2_access2; + u32 mc_generalized_carveout2_access3; + u32 mc_generalized_carveout2_access4; + u32 mc_generalized_carveout2_force_internal_access0; + u32 mc_generalized_carveout2_force_internal_access1; + u32 mc_generalized_carveout2_force_internal_access2; + u32 mc_generalized_carveout2_force_internal_access3; + u32 mc_generalized_carveout2_force_internal_access4; + u32 mc_generalized_carveout2_cfg0; + + u32 mc_generalized_carveout3_bom; + u32 mc_generalized_carveout3_bom_hi; + u32 mc_generalized_carveout3_size_128kb; + u32 mc_generalized_carveout3_access0; + u32 mc_generalized_carveout3_access1; + u32 mc_generalized_carveout3_access2; + u32 mc_generalized_carveout3_access3; + u32 mc_generalized_carveout3_access4; + u32 mc_generalized_carveout3_force_internal_access0; + u32 mc_generalized_carveout3_force_internal_access1; + u32 mc_generalized_carveout3_force_internal_access2; + u32 mc_generalized_carveout3_force_internal_access3; + u32 mc_generalized_carveout3_force_internal_access4; + u32 mc_generalized_carveout3_cfg0; + + u32 mc_generalized_carveout4_bom; + u32 mc_generalized_carveout4_bom_hi; + u32 mc_generalized_carveout4_size_128kb; + u32 mc_generalized_carveout4_access0; + u32 mc_generalized_carveout4_access1; + u32 mc_generalized_carveout4_access2; + u32 mc_generalized_carveout4_access3; + u32 mc_generalized_carveout4_access4; + u32 mc_generalized_carveout4_force_internal_access0; + u32 mc_generalized_carveout4_force_internal_access1; + u32 mc_generalized_carveout4_force_internal_access2; + u32 mc_generalized_carveout4_force_internal_access3; + u32 mc_generalized_carveout4_force_internal_access4; + u32 mc_generalized_carveout4_cfg0; + + u32 mc_generalized_carveout5_bom; + u32 mc_generalized_carveout5_bom_hi; + u32 mc_generalized_carveout5_size_128kb; + u32 mc_generalized_carveout5_access0; + u32 mc_generalized_carveout5_access1; + u32 mc_generalized_carveout5_access2; + u32 mc_generalized_carveout5_access3; + u32 mc_generalized_carveout5_access4; + u32 mc_generalized_carveout5_force_internal_access0; + u32 mc_generalized_carveout5_force_internal_access1; + u32 mc_generalized_carveout5_force_internal_access2; + u32 mc_generalized_carveout5_force_internal_access3; + u32 mc_generalized_carveout5_force_internal_access4; + u32 mc_generalized_carveout5_cfg0; + + /* Specifies enable for CA training */ + u32 emc_ca_training_enable; + /* Set if bit 6 select is greater than bit 7 select; uses aremc.spec packet SWIZZLE_BIT6_GT_BIT7 */ + u32 swizzle_rank_byte_encode; + /* Specifies enable and offset for patched boot rom write */ + u32 boot_rom_patch_control; + /* Specifies data for patched boot rom write */ + u32 boot_rom_patch_data; + + /* Specifies the value for MC_MTS_CARVEOUT_BOM */ + u32 mc_mts_carveout_bom; + /* Specifies the value for MC_MTS_CARVEOUT_ADR_HI */ + u32 mc_mts_carveout_adr_hi; + /* Specifies the value for MC_MTS_CARVEOUT_SIZE_MB */ + u32 mc_mts_carveout_size_mb; + /* Specifies the value for MC_MTS_CARVEOUT_REG_CTRL */ + u32 mc_mts_carveout_reg_ctrl; +} sdram_params_t; + +#endif diff --git a/src/hwinit/sdram_param_t210_lp0.h b/src/hwinit/sdram_param_t210_lp0.h new file mode 100644 index 0000000..9f33b16 --- /dev/null +++ b/src/hwinit/sdram_param_t210_lp0.h @@ -0,0 +1,961 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +/** + * Defines the SDRAM parameter structure. + * + * Note that PLLM is used by EMC. The field names are in camel case to ease + * directly converting BCT config files (*.cfg) into C structure. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ +#define __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ + +#include "types.h" + +enum { + /* Specifies the memory type to be undefined */ + NvBootMemoryType_None = 0, + + /* Specifies the memory type to be DDR SDRAM */ + NvBootMemoryType_Ddr = 0, + + /* Specifies the memory type to be LPDDR SDRAM */ + NvBootMemoryType_LpDdr = 0, + + /* Specifies the memory type to be DDR2 SDRAM */ + NvBootMemoryType_Ddr2 = 0, + + /* Specifies the memory type to be LPDDR2 SDRAM */ + NvBootMemoryType_LpDdr2, + + /* Specifies the memory type to be DDR3 SDRAM */ + NvBootMemoryType_Ddr3, + + /* Specifies the memory type to be LPDDR4 SDRAM */ + NvBootMemoryType_LpDdr4, + + NvBootMemoryType_Num, + + /* Specifies an entry in the ram_code table that's not in use */ + NvBootMemoryType_Unused = 0X7FFFFFF, +}; + +/** + * Defines the SDRAM parameter structure + */ +struct sdram_params { + + /* Specifies the type of memory device */ + u32 MemoryType; + + /* MC/EMC clock source configuration */ + + /* Specifies the M value for PllM */ + u32 PllMInputDivider; + /* Specifies the N value for PllM */ + u32 PllMFeedbackDivider; + /* Specifies the time to wait for PLLM to lock (in microseconds) */ + u32 PllMStableTime; + /* Specifies misc. control bits */ + u32 PllMSetupControl; + /* Specifies the P value for PLLM */ + u32 PllMPostDivider; + /* Specifies value for Charge Pump Gain Control */ + u32 PllMKCP; + /* Specifies VCO gain */ + u32 PllMKVCO; + /* Spare BCT param */ + u32 EmcBctSpare0; + /* Spare BCT param */ + u32 EmcBctSpare1; + /* Spare BCT param */ + u32 EmcBctSpare2; + /* Spare BCT param */ + u32 EmcBctSpare3; + /* Spare BCT param */ + u32 EmcBctSpare4; + /* Spare BCT param */ + u32 EmcBctSpare5; + /* Spare BCT param */ + u32 EmcBctSpare6; + /* Spare BCT param */ + u32 EmcBctSpare7; + /* Spare BCT param */ + u32 EmcBctSpare8; + /* Spare BCT param */ + u32 EmcBctSpare9; + /* Spare BCT param */ + u32 EmcBctSpare10; + /* Spare BCT param */ + u32 EmcBctSpare11; + /* Spare BCT param */ + u32 EmcBctSpare12; + /* Spare BCT param */ + u32 EmcBctSpare13; + + /* Defines EMC_2X_CLK_SRC, EMC_2X_CLK_DIVISOR, EMC_INVERT_DCD */ + u32 EmcClockSource; + u32 EmcClockSourceDll; + + /* Defines possible override for PLLLM_MISC2 */ + u32 ClkRstControllerPllmMisc2Override; + /* enables override for PLLLM_MISC2 */ + u32 ClkRstControllerPllmMisc2OverrideEnable; + /* defines CLK_ENB_MC1 in register clk_rst_controller_clk_enb_w_clr */ + u32 ClearClk2Mc1; + + /* Auto-calibration of EMC pads */ + + /* Specifies the value for EMC_AUTO_CAL_INTERVAL */ + u32 EmcAutoCalInterval; + /* + * Specifies the value for EMC_AUTO_CAL_CONFIG + * Note: Trigger bits are set by the SDRAM code. + */ + u32 EmcAutoCalConfig; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG2 */ + u32 EmcAutoCalConfig2; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG3 */ + u32 EmcAutoCalConfig3; + + /* Specifies the values for EMC_AUTO_CAL_CONFIG4-8 */ + u32 EmcAutoCalConfig4; + u32 EmcAutoCalConfig5; + u32 EmcAutoCalConfig6; + u32 EmcAutoCalConfig7; + u32 EmcAutoCalConfig8; + + /* Specifies the value for EMC_AUTO_CAL_VREF_SEL_0 */ + u32 EmcAutoCalVrefSel0; + u32 EmcAutoCalVrefSel1; + + /* Specifies the value for EMC_AUTO_CAL_CHANNEL */ + u32 EmcAutoCalChannel; + + /* Specifies the value for EMC_PMACRO_AUTOCAL_CFG_0 */ + u32 EmcPmacroAutocalCfg0; + u32 EmcPmacroAutocalCfg1; + u32 EmcPmacroAutocalCfg2; + u32 EmcPmacroRxTerm; + u32 EmcPmacroDqTxDrv; + u32 EmcPmacroCaTxDrv; + u32 EmcPmacroCmdTxDrv; + u32 EmcPmacroAutocalCfgCommon; + u32 EmcPmacroZctrl; + + /* + * Specifies the time for the calibration + * to stabilize (in microseconds) + */ + u32 EmcAutoCalWait; + + u32 EmcXm2CompPadCtrl; + u32 EmcXm2CompPadCtrl2; + u32 EmcXm2CompPadCtrl3; + + /* + * DRAM size information + * Specifies the value for EMC_ADR_CFG + */ + u32 EmcAdrCfg; + + /* + * Specifies the time to wait after asserting pin + * CKE (in microseconds) + */ + u32 EmcPinProgramWait; + /* Specifies the extra delay before/after pin RESET/CKE command */ + u32 EmcPinExtraWait; + + u32 EmcPinGpioEn; + u32 EmcPinGpio; + + /* + * Specifies the extra delay after the first writing + * of EMC_TIMING_CONTROL + */ + u32 EmcTimingControlWait; + + /* Timing parameters required for the SDRAM */ + + /* Specifies the value for EMC_RC */ + u32 EmcRc; + /* Specifies the value for EMC_RFC */ + u32 EmcRfc; + /* Specifies the value for EMC_RFC_PB */ + u32 EmcRfcPb; + /* Specifies the value for EMC_RFC_CTRL2 */ + u32 EmcRefctrl2; + /* Specifies the value for EMC_RFC_SLR */ + u32 EmcRfcSlr; + /* Specifies the value for EMC_RAS */ + u32 EmcRas; + /* Specifies the value for EMC_RP */ + u32 EmcRp; + /* Specifies the value for EMC_R2R */ + u32 EmcR2r; + /* Specifies the value for EMC_W2W */ + u32 EmcW2w; + /* Specifies the value for EMC_R2W */ + u32 EmcR2w; + /* Specifies the value for EMC_W2R */ + u32 EmcW2r; + /* Specifies the value for EMC_R2P */ + u32 EmcR2p; + /* Specifies the value for EMC_W2P */ + u32 EmcW2p; + + u32 EmcTppd; + u32 EmcCcdmw; + + /* Specifies the value for EMC_RD_RCD */ + u32 EmcRdRcd; + /* Specifies the value for EMC_WR_RCD */ + u32 EmcWrRcd; + /* Specifies the value for EMC_RRD */ + u32 EmcRrd; + /* Specifies the value for EMC_REXT */ + u32 EmcRext; + /* Specifies the value for EMC_WEXT */ + u32 EmcWext; + /* Specifies the value for EMC_WDV */ + u32 EmcWdv; + + u32 EmcWdvChk; + u32 EmcWsv; + u32 EmcWev; + + /* Specifies the value for EMC_WDV_MASK */ + u32 EmcWdvMask; + + u32 EmcWsDuration; + u32 EmcWeDuration; + + /* Specifies the value for EMC_QUSE */ + u32 EmcQUse; + /* Specifies the value for EMC_QUSE_WIDTH */ + u32 EmcQuseWidth; + /* Specifies the value for EMC_IBDLY */ + u32 EmcIbdly; + /* Specifies the value for EMC_OBDLY */ + u32 EmcObdly; + /* Specifies the value for EMC_EINPUT */ + u32 EmcEInput; + /* Specifies the value for EMC_EINPUT_DURATION */ + u32 EmcEInputDuration; + /* Specifies the value for EMC_PUTERM_EXTRA */ + u32 EmcPutermExtra; + /* Specifies the value for EMC_PUTERM_WIDTH */ + u32 EmcPutermWidth; + /* Specifies the value for EMC_PUTERM_ADJ */ + ////u32 EmcPutermAdj; + + /* Specifies the value for EMC_QRST */ + u32 EmcQRst; + /* Specifies the value for EMC_QSAFE */ + u32 EmcQSafe; + /* Specifies the value for EMC_RDV */ + u32 EmcRdv; + /* Specifies the value for EMC_RDV_MASK */ + u32 EmcRdvMask; + /* Specifies the value for EMC_RDV_EARLY */ + u32 EmcRdvEarly; + /* Specifies the value for EMC_RDV_EARLY_MASK */ + u32 EmcRdvEarlyMask; + /* Specifies the value for EMC_QPOP */ + u32 EmcQpop; + + /* Specifies the value for EMC_REFRESH */ + u32 EmcRefresh; + /* Specifies the value for EMC_BURST_REFRESH_NUM */ + u32 EmcBurstRefreshNum; + /* Specifies the value for EMC_PRE_REFRESH_REQ_CNT */ + u32 EmcPreRefreshReqCnt; + /* Specifies the value for EMC_PDEX2WR */ + u32 EmcPdEx2Wr; + /* Specifies the value for EMC_PDEX2RD */ + u32 EmcPdEx2Rd; + /* Specifies the value for EMC_PCHG2PDEN */ + u32 EmcPChg2Pden; + /* Specifies the value for EMC_ACT2PDEN */ + u32 EmcAct2Pden; + /* Specifies the value for EMC_AR2PDEN */ + u32 EmcAr2Pden; + /* Specifies the value for EMC_RW2PDEN */ + u32 EmcRw2Pden; + /* Specifies the value for EMC_CKE2PDEN */ + u32 EmcCke2Pden; + /* Specifies the value for EMC_PDEX2CKE */ + u32 EmcPdex2Cke; + /* Specifies the value for EMC_PDEX2MRR */ + u32 EmcPdex2Mrr; + /* Specifies the value for EMC_TXSR */ + u32 EmcTxsr; + /* Specifies the value for EMC_TXSRDLL */ + u32 EmcTxsrDll; + /* Specifies the value for EMC_TCKE */ + u32 EmcTcke; + /* Specifies the value for EMC_TCKESR */ + u32 EmcTckesr; + /* Specifies the value for EMC_TPD */ + u32 EmcTpd; + /* Specifies the value for EMC_TFAW */ + u32 EmcTfaw; + /* Specifies the value for EMC_TRPAB */ + u32 EmcTrpab; + /* Specifies the value for EMC_TCLKSTABLE */ + u32 EmcTClkStable; + /* Specifies the value for EMC_TCLKSTOP */ + u32 EmcTClkStop; + /* Specifies the value for EMC_TREFBW */ + u32 EmcTRefBw; + + /* FBIO configuration values */ + + /* Specifies the value for EMC_FBIO_CFG5 */ + u32 EmcFbioCfg5; + /* Specifies the value for EMC_FBIO_CFG7 */ + u32 EmcFbioCfg7; + /* Specifies the value for EMC_FBIO_CFG8 */ + u32 EmcFbioCfg8; + + /* Command mapping for CMD brick 0 */ + u32 EmcCmdMappingCmd0_0; + u32 EmcCmdMappingCmd0_1; + u32 EmcCmdMappingCmd0_2; + u32 EmcCmdMappingCmd1_0; + u32 EmcCmdMappingCmd1_1; + u32 EmcCmdMappingCmd1_2; + u32 EmcCmdMappingCmd2_0; + u32 EmcCmdMappingCmd2_1; + u32 EmcCmdMappingCmd2_2; + u32 EmcCmdMappingCmd3_0; + u32 EmcCmdMappingCmd3_1; + u32 EmcCmdMappingCmd3_2; + u32 EmcCmdMappingByte; + + /* Specifies the value for EMC_FBIO_SPARE */ + u32 EmcFbioSpare; + + /* Specifies the value for EMC_CFG_RSV */ + u32 EmcCfgRsv; + + /* MRS command values */ + + /* Specifies the value for EMC_MRS */ + u32 EmcMrs; + /* Specifies the MP0 command to initialize mode registers */ + u32 EmcEmrs; + /* Specifies the MP2 command to initialize mode registers */ + u32 EmcEmrs2; + /* Specifies the MP3 command to initialize mode registers */ + u32 EmcEmrs3; + /* Specifies the programming to LPDDR2 Mode Register 1 at cold boot */ + u32 EmcMrw1; + /* Specifies the programming to LPDDR2 Mode Register 2 at cold boot */ + u32 EmcMrw2; + /* Specifies the programming to LPDDR2 Mode Register 3 at cold boot */ + u32 EmcMrw3; + /* Specifies the programming to LPDDR2 Mode Register 11 at cold boot */ + u32 EmcMrw4; + /* Specifies the programming to LPDDR2 Mode Register 3? at cold boot */ + u32 EmcMrw6; + /* Specifies the programming to LPDDR2 Mode Register 11 at cold boot */ + u32 EmcMrw8; + /* Specifies the programming to LPDDR2 Mode Register 11? at cold boot */ + u32 EmcMrw9; + /* Specifies the programming to LPDDR2 Mode Register 12 at cold boot */ + u32 EmcMrw10; + /* Specifies the programming to LPDDR2 Mode Register 14 at cold boot */ + u32 EmcMrw12; + /* Specifies the programming to LPDDR2 Mode Register 14? at cold boot */ + u32 EmcMrw13; + /* Specifies the programming to LPDDR2 Mode Register 22 at cold boot */ + u32 EmcMrw14; + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at cold boot + */ + u32 EmcMrwExtra; + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at warm boot + */ + u32 EmcWarmBootMrwExtra; + /* + * Specify the enable of extra Mode Register programming at + * warm boot + */ + u32 EmcWarmBootExtraModeRegWriteEnable; + /* + * Specify the enable of extra Mode Register programming at + * cold boot + */ + u32 EmcExtraModeRegWriteEnable; + + /* Specifies the EMC_MRW reset command value */ + u32 EmcMrwResetCommand; + /* Specifies the EMC Reset wait time (in microseconds) */ + u32 EmcMrwResetNInitWait; + /* Specifies the value for EMC_MRS_WAIT_CNT */ + u32 EmcMrsWaitCnt; + /* Specifies the value for EMC_MRS_WAIT_CNT2 */ + u32 EmcMrsWaitCnt2; + + /* EMC miscellaneous configurations */ + + /* Specifies the value for EMC_CFG */ + u32 EmcCfg; + /* Specifies the value for EMC_CFG_2 */ + u32 EmcCfg2; + /* Specifies the pipe bypass controls */ + u32 EmcCfgPipe; + u32 EmcCfgPipeClk; + u32 EmcFdpdCtrlCmdNoRamp; + u32 EmcCfgUpdate; + + /* Specifies the value for EMC_DBG */ + u32 EmcDbg; + u32 EmcDbgWriteMux; + + /* Specifies the value for EMC_CMDQ */ + u32 EmcCmdQ; + /* Specifies the value for EMC_MC2EMCQ */ + u32 EmcMc2EmcQ; + /* Specifies the value for EMC_DYN_SELF_REF_CONTROL */ + u32 EmcDynSelfRefControl; + + /* Specifies the value for MEM_INIT_DONE */ + u32 AhbArbitrationXbarCtrlMemInitDone; + + /* Specifies the value for EMC_CFG_DIG_DLL */ + u32 EmcCfgDigDll; + u32 EmcCfgDigDll_1; + /* Specifies the value for EMC_CFG_DIG_DLL_PERIOD */ + u32 EmcCfgDigDllPeriod; + /* Specifies the value of *DEV_SELECTN of various EMC registers */ + u32 EmcDevSelect; + + /* Specifies the value for EMC_SEL_DPD_CTRL */ + u32 EmcSelDpdCtrl; + + /* Pads trimmer delays */ + u32 EmcFdpdCtrlDq; + u32 EmcFdpdCtrlCmd; + u32 EmcPmacroIbVrefDq_0; + u32 EmcPmacroIbVrefDq_1; + u32 EmcPmacroIbVrefDqs_0; + u32 EmcPmacroIbVrefDqs_1; + u32 EmcPmacroIbRxrt; + u32 EmcCfgPipe1; + u32 EmcCfgPipe2; + + /* Specifies the value for EMC_PMACRO_QUSE_DDLL_RANK0_0 */ + u32 EmcPmacroQuseDdllRank0_0; + u32 EmcPmacroQuseDdllRank0_1; + u32 EmcPmacroQuseDdllRank0_2; + u32 EmcPmacroQuseDdllRank0_3; + u32 EmcPmacroQuseDdllRank0_4; + u32 EmcPmacroQuseDdllRank0_5; + u32 EmcPmacroQuseDdllRank1_0; + u32 EmcPmacroQuseDdllRank1_1; + u32 EmcPmacroQuseDdllRank1_2; + u32 EmcPmacroQuseDdllRank1_3; + u32 EmcPmacroQuseDdllRank1_4; + u32 EmcPmacroQuseDdllRank1_5; + + u32 EmcPmacroObDdllLongDqRank0_0; + u32 EmcPmacroObDdllLongDqRank0_1; + u32 EmcPmacroObDdllLongDqRank0_2; + u32 EmcPmacroObDdllLongDqRank0_3; + u32 EmcPmacroObDdllLongDqRank0_4; + u32 EmcPmacroObDdllLongDqRank0_5; + u32 EmcPmacroObDdllLongDqRank1_0; + u32 EmcPmacroObDdllLongDqRank1_1; + u32 EmcPmacroObDdllLongDqRank1_2; + u32 EmcPmacroObDdllLongDqRank1_3; + u32 EmcPmacroObDdllLongDqRank1_4; + u32 EmcPmacroObDdllLongDqRank1_5; + + u32 EmcPmacroObDdllLongDqsRank0_0; + u32 EmcPmacroObDdllLongDqsRank0_1; + u32 EmcPmacroObDdllLongDqsRank0_2; + u32 EmcPmacroObDdllLongDqsRank0_3; + u32 EmcPmacroObDdllLongDqsRank0_4; + u32 EmcPmacroObDdllLongDqsRank0_5; + u32 EmcPmacroObDdllLongDqsRank1_0; + u32 EmcPmacroObDdllLongDqsRank1_1; + u32 EmcPmacroObDdllLongDqsRank1_2; + u32 EmcPmacroObDdllLongDqsRank1_3; + u32 EmcPmacroObDdllLongDqsRank1_4; + u32 EmcPmacroObDdllLongDqsRank1_5; + + u32 EmcPmacroIbDdllLongDqsRank0_0; + u32 EmcPmacroIbDdllLongDqsRank0_1; + u32 EmcPmacroIbDdllLongDqsRank0_2; + u32 EmcPmacroIbDdllLongDqsRank0_3; + u32 EmcPmacroIbDdllLongDqsRank1_0; + u32 EmcPmacroIbDdllLongDqsRank1_1; + u32 EmcPmacroIbDdllLongDqsRank1_2; + u32 EmcPmacroIbDdllLongDqsRank1_3; + + u32 EmcPmacroDdllLongCmd_0; + u32 EmcPmacroDdllLongCmd_1; + u32 EmcPmacroDdllLongCmd_2; + u32 EmcPmacroDdllLongCmd_3; + u32 EmcPmacroDdllLongCmd_4; + u32 EmcPmacroDdllShortCmd_0; + u32 EmcPmacroDdllShortCmd_1; + u32 EmcPmacroDdllShortCmd_2; + + /* + * Specifies the delay after asserting CKE pin during a WarmBoot0 + * sequence (in microseconds) + */ + u32 WarmBootWait; + + /* Specifies the value for EMC_ODT_WRITE */ + u32 EmcOdtWrite; + + /* Periodic ZQ calibration */ + + /* + * Specifies the value for EMC_ZCAL_INTERVAL + * Value 0 disables ZQ calibration + */ + u32 EmcZcalInterval; + /* Specifies the value for EMC_ZCAL_WAIT_CNT */ + u32 EmcZcalWaitCnt; + /* Specifies the value for EMC_ZCAL_MRW_CMD */ + u32 EmcZcalMrwCmd; + + /* DRAM initialization sequence flow control */ + + /* Specifies the MRS command value for resetting DLL */ + u32 EmcMrsResetDll; + /* Specifies the command for ZQ initialization of device 0 */ + u32 EmcZcalInitDev0; + /* Specifies the command for ZQ initialization of device 1 */ + u32 EmcZcalInitDev1; + /* + * Specifies the wait time after programming a ZQ initialization + * command (in microseconds) + */ + u32 EmcZcalInitWait; + /* + * Specifies the enable for ZQ calibration at cold boot [bit 0] + * and warm boot [bit 1] + */ + u32 EmcZcalWarmColdBootEnables; + + /* + * Specifies the MRW command to LPDDR2 for ZQ calibration + * on warmboot + */ + /* Is issued to both devices separately */ + u32 EmcMrwLpddr2ZcalWarmBoot; + /* + * Specifies the ZQ command to DDR3 for ZQ calibration on warmboot + * Is issued to both devices separately + */ + u32 EmcZqCalDdr3WarmBoot; + u32 EmcZqCalLpDdr4WarmBoot; + /* + * Specifies the wait time for ZQ calibration on warmboot + * (in microseconds) + */ + u32 EmcZcalWarmBootWait; + /* + * Specifies the enable for DRAM Mode Register programming + * at warm boot + */ + u32 EmcMrsWarmBootEnable; + /* + * Specifies the wait time after sending an MRS DLL reset command + * in microseconds) + */ + u32 EmcMrsResetDllWait; + /* Specifies the extra MRS command to initialize mode registers */ + u32 EmcMrsExtra; + /* Specifies the extra MRS command at warm boot */ + u32 EmcWarmBootMrsExtra; + /* Specifies the EMRS command to enable the DDR2 DLL */ + u32 EmcEmrsDdr2DllEnable; + /* Specifies the MRS command to reset the DDR2 DLL */ + u32 EmcMrsDdr2DllReset; + /* Specifies the EMRS command to set OCD calibration */ + u32 EmcEmrsDdr2OcdCalib; + /* + * Specifies the wait between initializing DDR and setting OCD + * calibration (in microseconds) + */ + u32 EmcDdr2Wait; + /* Specifies the value for EMC_CLKEN_OVERRIDE */ + u32 EmcClkenOverride; + + /* + * Specifies LOG2 of the extra refresh numbers after booting + * Program 0 to disable + */ + u32 EmcExtraRefreshNum; + /* Specifies the master override for all EMC clocks */ + u32 EmcClkenOverrideAllWarmBoot; + /* Specifies the master override for all MC clocks */ + u32 McClkenOverrideAllWarmBoot; + /* Specifies digital dll period, choosing between 4 to 64 ms */ + u32 EmcCfgDigDllPeriodWarmBoot; + + /* Pad controls */ + + /* Specifies the value for PMC_VDDP_SEL */ + u32 PmcVddpSel; + /* Specifies the wait time after programming PMC_VDDP_SEL */ + u32 PmcVddpSelWait; + /* Specifies the value for PMC_DDR_PWR */ + u32 PmcDdrPwr; + /* Specifies the value for PMC_DDR_CFG */ + u32 PmcDdrCfg; + /* Specifies the value for PMC_IO_DPD3_REQ */ + u32 PmcIoDpd3Req; + /* Specifies the wait time after programming PMC_IO_DPD3_REQ */ + u32 PmcIoDpd3ReqWait; + u32 PmcIoDpd4ReqWait; + + /* Specifies the value for PMC_REG_SHORT */ + u32 PmcRegShort; + /* Specifies the value for PMC_NO_IOPOWER */ + u32 PmcNoIoPower; + + u32 PmcDdrCntrlWait; + u32 PmcDdrCntrl; + + /* Specifies the value for EMC_ACPD_CONTROL */ + u32 EmcAcpdControl; + + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE_CFG */ + ////u32 EmcSwizzleRank0ByteCfg; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE0 */ + u32 EmcSwizzleRank0Byte0; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE1 */ + u32 EmcSwizzleRank0Byte1; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE2 */ + u32 EmcSwizzleRank0Byte2; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE3 */ + u32 EmcSwizzleRank0Byte3; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE_CFG */ + ////u32 EmcSwizzleRank1ByteCfg; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE0 */ + u32 EmcSwizzleRank1Byte0; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE1 */ + u32 EmcSwizzleRank1Byte1; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE2 */ + u32 EmcSwizzleRank1Byte2; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE3 */ + u32 EmcSwizzleRank1Byte3; + + /* Specifies the value for EMC_TXDSRVTTGEN */ + u32 EmcTxdsrvttgen; + + /* Specifies the value for EMC_DATA_BRLSHFT_0 */ + u32 EmcDataBrlshft0; + u32 EmcDataBrlshft1; + + u32 EmcDqsBrlshft0; + u32 EmcDqsBrlshft1; + + u32 EmcCmdBrlshft0; + u32 EmcCmdBrlshft1; + u32 EmcCmdBrlshft2; + u32 EmcCmdBrlshft3; + + u32 EmcQuseBrlshft0; + u32 EmcQuseBrlshft1; + u32 EmcQuseBrlshft2; + u32 EmcQuseBrlshft3; + + u32 EmcDllCfg0; + u32 EmcDllCfg1; + + u32 EmcPmcScratch1; + u32 EmcPmcScratch2; + u32 EmcPmcScratch3; + + u32 EmcPmacroPadCfgCtrl; + + u32 EmcPmacroVttgenCtrl0; + u32 EmcPmacroVttgenCtrl1; + u32 EmcPmacroVttgenCtrl2; + + u32 EmcPmacroBrickCtrlRfu1; + u32 EmcPmacroCmdBrickCtrlFdpd; + u32 EmcPmacroBrickCtrlRfu2; + u32 EmcPmacroDataBrickCtrlFdpd; + u32 EmcPmacroBgBiasCtrl0; + u32 EmcPmacroDataPadRxCtrl; + u32 EmcPmacroCmdPadRxCtrl; + u32 EmcPmacroDataRxTermMode; + u32 EmcPmacroCmdRxTermMode; + u32 EmcPmacroDataPadTxCtrl; + u32 EmcPmacroCommonPadTxCtrl; + u32 EmcPmacroCmdPadTxCtrl; + u32 EmcCfg3; + + u32 EmcPmacroTxPwrd0; + u32 EmcPmacroTxPwrd1; + u32 EmcPmacroTxPwrd2; + u32 EmcPmacroTxPwrd3; + u32 EmcPmacroTxPwrd4; + u32 EmcPmacroTxPwrd5; + + u32 EmcConfigSampleDelay; + + u32 EmcPmacroBrickMapping0; + u32 EmcPmacroBrickMapping1; + u32 EmcPmacroBrickMapping2; + + u32 EmcPmacroTxSelClkSrc0; + u32 EmcPmacroTxSelClkSrc1; + u32 EmcPmacroTxSelClkSrc2; + u32 EmcPmacroTxSelClkSrc3; + u32 EmcPmacroTxSelClkSrc4; + u32 EmcPmacroTxSelClkSrc5; + + u32 EmcPmacroDdllBypass; + + u32 EmcPmacroDdllPwrd0; + u32 EmcPmacroDdllPwrd1; + u32 EmcPmacroDdllPwrd2; + + u32 EmcPmacroCmdCtrl0; + u32 EmcPmacroCmdCtrl1; + u32 EmcPmacroCmdCtrl2; + + /* DRAM size information */ + + /* Specifies the value for MC_EMEM_ADR_CFG */ + u32 McEmemAdrCfg; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV0 */ + u32 McEmemAdrCfgDev0; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV1 */ + u32 McEmemAdrCfgDev1; + u32 McEmemAdrCfgChannelMask; + + /* Specifies the value for MC_EMEM_BANK_SWIZZLECfg0 */ + u32 McEmemAdrCfgBankMask0; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG1 */ + u32 McEmemAdrCfgBankMask1; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG2 */ + u32 McEmemAdrCfgBankMask2; + + /* + * Specifies the value for MC_EMEM_CFG which holds the external memory + * size (in KBytes) + */ + u32 McEmemCfg; + + /* MC arbitration configuration */ + + /* Specifies the value for MC_EMEM_ARB_CFG */ + u32 McEmemArbCfg; + /* Specifies the value for MC_EMEM_ARB_OUTSTANDING_REQ */ + u32 McEmemArbOutstandingReq; + + u32 McEmemArbRefpbHpCtrl; + u32 McEmemArbRefpbBankCtrl; + + /* Specifies the value for MC_EMEM_ARB_TIMING_RCD */ + u32 McEmemArbTimingRcd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RP */ + u32 McEmemArbTimingRp; + /* Specifies the value for MC_EMEM_ARB_TIMING_RC */ + u32 McEmemArbTimingRc; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAS */ + u32 McEmemArbTimingRas; + /* Specifies the value for MC_EMEM_ARB_TIMING_FAW */ + u32 McEmemArbTimingFaw; + /* Specifies the value for MC_EMEM_ARB_TIMING_RRD */ + u32 McEmemArbTimingRrd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAP2PRE */ + u32 McEmemArbTimingRap2Pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_WAP2PRE */ + u32 McEmemArbTimingWap2Pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2R */ + u32 McEmemArbTimingR2R; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2W */ + u32 McEmemArbTimingW2W; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2W */ + u32 McEmemArbTimingR2W; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2R */ + u32 McEmemArbTimingW2R; + + u32 McEmemArbTimingRFCPB; + + /* Specifies the value for MC_EMEM_ARB_DA_TURNS */ + u32 McEmemArbDaTurns; + /* Specifies the value for MC_EMEM_ARB_DA_COVERS */ + u32 McEmemArbDaCovers; + /* Specifies the value for MC_EMEM_ARB_MISC0 */ + u32 McEmemArbMisc0; + /* Specifies the value for MC_EMEM_ARB_MISC1 */ + u32 McEmemArbMisc1; + u32 McEmemArbMisc2; + + /* Specifies the value for MC_EMEM_ARB_RING1_THROTTLE */ + u32 McEmemArbRing1Throttle; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE */ + u32 McEmemArbOverride; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE_1 */ + u32 McEmemArbOverride1; + /* Specifies the value for MC_EMEM_ARB_RSV */ + u32 McEmemArbRsv; + + u32 McDaCfg0; + u32 McEmemArbTimingCcdmw; + + /* Specifies the value for MC_CLKEN_OVERRIDE */ + u32 McClkenOverride; + + /* Specifies the value for MC_STAT_CONTROL */ + u32 McStatControl; + + /* Specifies the value for MC_VIDEO_PROTECT_BOM */ + u32 McVideoProtectBom; + /* Specifies the value for MC_VIDEO_PROTECT_BOM_ADR_HI */ + u32 McVideoProtectBomAdrHi; + /* Specifies the value for MC_VIDEO_PROTECT_SIZE_MB */ + u32 McVideoProtectSizeMb; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE */ + u32 McVideoProtectVprOverride; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE1 */ + u32 McVideoProtectVprOverride1; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_0 */ + u32 McVideoProtectGpuOverride0; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_1 */ + u32 McVideoProtectGpuOverride1; + /* Specifies the value for MC_SEC_CARVEOUT_BOM */ + u32 McSecCarveoutBom; + /* Specifies the value for MC_SEC_CARVEOUT_ADR_HI */ + u32 McSecCarveoutAdrHi; + /* Specifies the value for MC_SEC_CARVEOUT_SIZE_MB */ + u32 McSecCarveoutSizeMb; + /* Specifies the value for MC_VIDEO_PROTECT_REG_CTRL. + VIDEO_PROTECT_WRITEAccess */ + u32 McVideoProtectWriteAccess; + /* Specifies the value for MC_SEC_CARVEOUT_REG_CTRL. + SEC_CARVEOUT_WRITEAccess */ + u32 McSecCarveoutProtectWriteAccess; + + /* Write-Protect Regions (WPR) */ + u32 McGeneralizedCarveout1Bom; + u32 McGeneralizedCarveout1BomHi; + u32 McGeneralizedCarveout1Size128kb; + u32 McGeneralizedCarveout1Access0; + u32 McGeneralizedCarveout1Access1; + u32 McGeneralizedCarveout1Access2; + u32 McGeneralizedCarveout1Access3; + u32 McGeneralizedCarveout1Access4; + u32 McGeneralizedCarveout1ForceInternalAccess0; + u32 McGeneralizedCarveout1ForceInternalAccess1; + u32 McGeneralizedCarveout1ForceInternalAccess2; + u32 McGeneralizedCarveout1ForceInternalAccess3; + u32 McGeneralizedCarveout1ForceInternalAccess4; + u32 McGeneralizedCarveout1Cfg0; + + u32 McGeneralizedCarveout2Bom; + u32 McGeneralizedCarveout2BomHi; + u32 McGeneralizedCarveout2Size128kb; + u32 McGeneralizedCarveout2Access0; + u32 McGeneralizedCarveout2Access1; + u32 McGeneralizedCarveout2Access2; + u32 McGeneralizedCarveout2Access3; + u32 McGeneralizedCarveout2Access4; + u32 McGeneralizedCarveout2ForceInternalAccess0; + u32 McGeneralizedCarveout2ForceInternalAccess1; + u32 McGeneralizedCarveout2ForceInternalAccess2; + u32 McGeneralizedCarveout2ForceInternalAccess3; + u32 McGeneralizedCarveout2ForceInternalAccess4; + u32 McGeneralizedCarveout2Cfg0; + + u32 McGeneralizedCarveout3Bom; + u32 McGeneralizedCarveout3BomHi; + u32 McGeneralizedCarveout3Size128kb; + u32 McGeneralizedCarveout3Access0; + u32 McGeneralizedCarveout3Access1; + u32 McGeneralizedCarveout3Access2; + u32 McGeneralizedCarveout3Access3; + u32 McGeneralizedCarveout3Access4; + u32 McGeneralizedCarveout3ForceInternalAccess0; + u32 McGeneralizedCarveout3ForceInternalAccess1; + u32 McGeneralizedCarveout3ForceInternalAccess2; + u32 McGeneralizedCarveout3ForceInternalAccess3; + u32 McGeneralizedCarveout3ForceInternalAccess4; + u32 McGeneralizedCarveout3Cfg0; + + u32 McGeneralizedCarveout4Bom; + u32 McGeneralizedCarveout4BomHi; + u32 McGeneralizedCarveout4Size128kb; + u32 McGeneralizedCarveout4Access0; + u32 McGeneralizedCarveout4Access1; + u32 McGeneralizedCarveout4Access2; + u32 McGeneralizedCarveout4Access3; + u32 McGeneralizedCarveout4Access4; + u32 McGeneralizedCarveout4ForceInternalAccess0; + u32 McGeneralizedCarveout4ForceInternalAccess1; + u32 McGeneralizedCarveout4ForceInternalAccess2; + u32 McGeneralizedCarveout4ForceInternalAccess3; + u32 McGeneralizedCarveout4ForceInternalAccess4; + u32 McGeneralizedCarveout4Cfg0; + + u32 McGeneralizedCarveout5Bom; + u32 McGeneralizedCarveout5BomHi; + u32 McGeneralizedCarveout5Size128kb; + u32 McGeneralizedCarveout5Access0; + u32 McGeneralizedCarveout5Access1; + u32 McGeneralizedCarveout5Access2; + u32 McGeneralizedCarveout5Access3; + u32 McGeneralizedCarveout5Access4; + u32 McGeneralizedCarveout5ForceInternalAccess0; + u32 McGeneralizedCarveout5ForceInternalAccess1; + u32 McGeneralizedCarveout5ForceInternalAccess2; + u32 McGeneralizedCarveout5ForceInternalAccess3; + u32 McGeneralizedCarveout5ForceInternalAccess4; + u32 McGeneralizedCarveout5Cfg0; + + /* Specifies enable for CA training */ + u32 EmcCaTrainingEnable; + + /* Set if bit 6 select is greater than bit 7 select; uses aremc. + spec packet SWIZZLE_BIT6_GT_BIT7 */ + u32 SwizzleRankByteEncode; + /* Specifies enable and offset for patched boot ROM write */ + u32 BootRomPatchControl; + /* Specifies data for patched boot ROM write */ + u32 BootRomPatchData; + + /* Specifies the value for MC_MTS_CARVEOUT_BOM */ + u32 McMtsCarveoutBom; + /* Specifies the value for MC_MTS_CARVEOUT_ADR_HI */ + u32 McMtsCarveoutAdrHi; + /* Specifies the value for MC_MTS_CARVEOUT_SIZE_MB */ + u32 McMtsCarveoutSizeMb; + /* Specifies the value for MC_MTS_CARVEOUT_REG_CTRL */ + u32 McMtsCarveoutRegCtrl; + + /* End */ +}; + +#endif /* __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ */ \ No newline at end of file diff --git a/src/hwinit/se.c b/src/hwinit/se.c new file mode 100644 index 0000000..0b2d4aa --- /dev/null +++ b/src/hwinit/se.c @@ -0,0 +1,256 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "se.h" +#include "heap.h" +#include "t210.h" +#include "se_t210.h" + +typedef struct _se_ll_t +{ + vu32 num; + vu32 addr; + vu32 size; +} se_ll_t; + +static void _gf256_mul_x(void *block) +{ + u8 *pdata = (u8 *)block; + u32 carry = 0; + + for (u32 i = 0xF; i >= 0; i--) + { + u8 b = pdata[i]; + pdata[i] = (b << 1) | carry; + carry = b >> 7; + } + + if (carry) + pdata[0xF] ^= 0x87; +} + +static void _se_ll_init(se_ll_t *ll, u32 addr, u32 size) +{ + ll->num = 0; + ll->addr = addr; + ll->size = size; +} + +static void _se_ll_set(se_ll_t *dst, se_ll_t *src) +{ + SE(SE_IN_LL_ADDR_REG_OFFSET) = (u32)src; + SE(SE_OUT_LL_ADDR_REG_OFFSET) = (u32)dst; +} + +static int _se_wait() +{ + while (!(SE(SE_INT_STATUS_REG_OFFSET) & SE_INT_OP_DONE(INT_SET))) + ; + if (SE(SE_INT_STATUS_REG_OFFSET) & SE_INT_ERROR(INT_SET) || + SE(SE_STATUS_0) & 3 || + SE(SE_ERR_STATUS_0) != 0) + return 0; + return 1; +} + +static int _se_execute(u32 op, void *dst, u32 dst_size, const void *src, u32 src_size) +{ + se_ll_t *ll_dst = NULL, *ll_src = NULL; + + if (dst) + { + ll_dst = (se_ll_t *)malloc(sizeof(se_ll_t)); + _se_ll_init(ll_dst, (u32)dst, dst_size); + } + + if (src) + { + ll_src = (se_ll_t *)malloc(sizeof(se_ll_t)); + _se_ll_init(ll_src, (u32)src, src_size); + } + + _se_ll_set(ll_dst, ll_src); + + SE(SE_ERR_STATUS_0) = SE(SE_ERR_STATUS_0); + SE(SE_INT_STATUS_REG_OFFSET) = SE(SE_INT_STATUS_REG_OFFSET); + SE(SE_OPERATION_REG_OFFSET) = SE_OPERATION(op); + + int res = _se_wait(); + + if (src) + free(ll_src); + if (dst) + free(ll_dst); + + return res; +} + +static int _se_execute_one_block(u32 op, void *dst, u32 dst_size, const void *src, u32 src_size) +{ + u8 *block = (u8 *)malloc(0x10); + memset(block, 0, 0x10); + + SE(SE_BLOCK_COUNT_REG_OFFSET) = 0; + + memcpy(block, src, src_size); + int res = _se_execute(op, block, 0x10, block, 0x10); + memcpy(dst, block, dst_size); + + free(block); + return res; +} + +static void _se_aes_ctr_set(void *ctr) +{ + u32 *data = (u32 *)ctr; + for (u32 i = 0; i < 4; i++) + SE(SE_CRYPTO_CTR_REG_OFFSET + 4 * i) = data[i]; +} + +void se_rsa_acc_ctrl(u32 rs, u32 flags) +{ + if (flags & 0x7F) + SE(SE_RSA_KEYTABLE_ACCESS_REG_OFFSET + 4 * rs) = (((flags >> 4) & 4) | (flags & 3)) ^ 7; + if (flags & 0x80) + SE(SE_RSA_KEYTABLE_ACCESS_LOCK_OFFSET) &= ~(1 << rs); +} + +void se_key_acc_ctrl(u32 ks, u32 flags) +{ + if (flags & 0x7F) + SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 4 * ks) = ~flags; + if (flags & 0x80) + SE(SE_KEY_TABLE_ACCESS_LOCK_OFFSET) &= ~(1 << ks); +} + +void se_aes_key_set(u32 ks, void *key, u32 size) +{ + u32 *data = (u32 *)key; + for (u32 i = 0; i < size / 4; i++) + { + SE(SE_KEYTABLE_REG_OFFSET) = SE_KEYTABLE_SLOT(ks) | i; + SE(SE_KEYTABLE_DATA0_REG_OFFSET) = data[i]; + } +} + +void se_aes_key_clear(u32 ks) +{ + for (u32 i = 0; i < TEGRA_SE_AES_MAX_KEY_SIZE / 4; i++) + { + SE(SE_KEYTABLE_REG_OFFSET) = SE_KEYTABLE_SLOT(ks) | i; + SE(SE_KEYTABLE_DATA0_REG_OFFSET) = 0; + } +} + +int se_aes_unwrap_key(u32 ks_dst, u32 ks_src, const void *input) +{ + SE(SE_CONFIG_REG_OFFSET) = SE_CONFIG_DEC_ALG(ALG_AES_DEC) | SE_CONFIG_DST(DST_KEYTAB); + SE(SE_CRYPTO_REG_OFFSET) = SE_CRYPTO_KEY_INDEX(ks_src) | SE_CRYPTO_CORE_SEL(CORE_DECRYPT); + SE(SE_CRYPTO_KEYTABLE_DST_REG_OFFSET) = SE_CRYPTO_KEYTABLE_DST_KEY_INDEX(ks_dst); + return _se_execute(OP_START, NULL, 0, input, 0x10); +} + +int se_aes_crypt_block_ecb(u32 ks, u32 enc, void *dst, const void *src) +{ + if (enc) + { + SE(SE_CONFIG_REG_OFFSET) = SE_CONFIG_ENC_ALG(ALG_AES_ENC) | SE_CONFIG_DST(DST_MEMORY); + SE(SE_CRYPTO_REG_OFFSET) = SE_CRYPTO_KEY_INDEX(ks) | SE_CRYPTO_CORE_SEL(CORE_ENCRYPT); + } + else + { + SE(SE_CONFIG_REG_OFFSET) = SE_CONFIG_DEC_ALG(ALG_AES_DEC) | SE_CONFIG_DST(DST_MEMORY); + SE(SE_CRYPTO_REG_OFFSET) = SE_CRYPTO_KEY_INDEX(ks) | SE_CRYPTO_CORE_SEL(CORE_DECRYPT); + } + SE(SE_BLOCK_COUNT_REG_OFFSET) = 0; + return _se_execute(OP_START, dst, 0x10, src, 0x10); +} + +int se_aes_crypt_ctr(u32 ks, void *dst, u32 dst_size, const void *src, u32 src_size, void *ctr) +{ + SE(SE_SPARE_0_REG_OFFSET) = 1; + SE(SE_CONFIG_REG_OFFSET) = SE_CONFIG_ENC_ALG(ALG_AES_ENC) | SE_CONFIG_DST(DST_MEMORY); + SE(SE_CRYPTO_REG_OFFSET) = SE_CRYPTO_KEY_INDEX(ks) | SE_CRYPTO_CORE_SEL(CORE_ENCRYPT) | + SE_CRYPTO_XOR_POS(XOR_BOTTOM) | SE_CRYPTO_INPUT_SEL(INPUT_LNR_CTR) | SE_CRYPTO_CTR_VAL(1); + _se_aes_ctr_set(ctr); + + u32 src_size_aligned = src_size & 0xFFFFFFF0; + u32 src_size_delta = src_size & 0xF; + + if (src_size_aligned) + { + SE(SE_BLOCK_COUNT_REG_OFFSET) = (src_size >> 4) - 1; + if (!_se_execute(OP_START, dst, dst_size, src, src_size_aligned)) + return 0; + } + + if (src_size - src_size_aligned && src_size_aligned < dst_size) + return _se_execute_one_block(OP_START, dst + src_size_aligned, + MIN(src_size_delta, dst_size - src_size_aligned), + src + src_size_aligned, src_size_delta); + + return 1; +} + +int se_aes_xts_crypt_sec(u32 ks1, u32 ks2, u32 enc, u64 sec, void *dst, void *src, u32 secsize) +{ + int res = 0; + u8 *tweak = (u8 *)malloc(0x10); + u8 *pdst = (u8 *)dst; + u8 *psrc = (u8 *)src; + + //Generate tweak. + for (int i = 0xF; i >= 0; i--) + { + tweak[i] = sec & 0xFF; + sec >>= 8; + } + if (!se_aes_crypt_block_ecb(ks1, 1, tweak, tweak)) + goto out; + + //We are assuming a 0x10-aligned sector size in this implementation. + for (u32 i = 0; i < secsize / 0x10; i++) + { + for (u32 j = 0; j < 0x10; j++) + pdst[j] = psrc[j] ^ tweak[j]; + if (!se_aes_crypt_block_ecb(ks2, enc, pdst, pdst)) + goto out; + for (u32 j = 0; j < 0x10; j++) + pdst[j] = pdst[j] ^ tweak[j]; + _gf256_mul_x(tweak); + psrc += 0x10; + pdst += 0x10; + } + + res = 1; + +out:; + free(tweak); + return res; +} + +int se_aes_xts_crypt(u32 ks1, u32 ks2, u32 enc, u64 sec, void *dst, void *src, u32 secsize, u32 num_secs) +{ + u8 *pdst = (u8 *)dst; + u8 *psrc = (u8 *)src; + + for (u32 i = 0; i < num_secs; i++) + if (!se_aes_xts_crypt_sec(ks1, ks2, enc, sec + i, pdst + secsize * i, psrc + secsize * i, secsize)) + return 0; + + return 1; +} diff --git a/src/hwinit/se.h b/src/hwinit/se.h new file mode 100644 index 0000000..b58058c --- /dev/null +++ b/src/hwinit/se.h @@ -0,0 +1,30 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _SE_H_ +#define _SE_H_ + +#include "types.h" + +void se_rsa_acc_ctrl(u32 rs, u32 flags); +void se_key_acc_ctrl(u32 ks, u32 flags); +void se_aes_key_set(u32 ks, void *key, u32 size); +void se_aes_key_clear(u32 ks); +int se_aes_unwrap_key(u32 ks_dst, u32 ks_src, const void *input); +int se_aes_crypt_block_ecb(u32 ks, u32 enc, void *dst, const void *src); +int se_aes_crypt_ctr(u32 ks, void *dst, u32 dst_size, const void *src, u32 src_size, void *ctr); + +#endif diff --git a/src/hwinit/se_t210.h b/src/hwinit/se_t210.h new file mode 100644 index 0000000..e1bd968 --- /dev/null +++ b/src/hwinit/se_t210.h @@ -0,0 +1,357 @@ +/* +* Driver for Tegra Security Engine +* +* Copyright (c) 2011-2013, NVIDIA Corporation. 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., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _CRYPTO_TEGRA_SE_H +#define _CRYPTO_TEGRA_SE_H + +#include "types.h" + +#define TEGRA_SE_CRA_PRIORITY 300 +#define TEGRA_SE_COMPOSITE_PRIORITY 400 +#define TEGRA_SE_CRYPTO_QUEUE_LENGTH 50 +#define SE_MAX_SRC_SG_COUNT 50 +#define SE_MAX_DST_SG_COUNT 50 + +#define TEGRA_SE_KEYSLOT_COUNT 16 +#define SE_MAX_LAST_BLOCK_SIZE 0xFFFFF + +/* SE register definitions */ +#define SE_SECURITY_0 0x000 +#define SE_KEY_SCHED_READ_SHIFT 3 + +#define SE_CONFIG_REG_OFFSET 0x014 +#define SE_CONFIG_ENC_ALG_SHIFT 12 +#define SE_CONFIG_DEC_ALG_SHIFT 8 +#define ALG_AES_ENC 1 +#define ALG_RNG 2 +#define ALG_SHA 3 +#define ALG_RSA 4 +#define ALG_NOP 0 +#define ALG_AES_DEC 1 +#define SE_CONFIG_ENC_ALG(x) (x << SE_CONFIG_ENC_ALG_SHIFT) +#define SE_CONFIG_DEC_ALG(x) (x << SE_CONFIG_DEC_ALG_SHIFT) +#define SE_CONFIG_DST_SHIFT 2 +#define DST_MEMORY 0 +#define DST_HASHREG 1 +#define DST_KEYTAB 2 +#define DST_SRK 3 +#define DST_RSAREG 4 +#define SE_CONFIG_DST(x) (x << SE_CONFIG_DST_SHIFT) +#define SE_CONFIG_ENC_MODE_SHIFT 24 +#define SE_CONFIG_DEC_MODE_SHIFT 16 +#define MODE_KEY128 0 +#define MODE_KEY192 1 +#define MODE_KEY256 2 +#define MODE_SHA1 0 +#define MODE_SHA224 4 +#define MODE_SHA256 5 +#define MODE_SHA384 6 +#define MODE_SHA512 7 +#define SE_CONFIG_ENC_MODE(x) (x << SE_CONFIG_ENC_MODE_SHIFT) +#define SE_CONFIG_DEC_MODE(x) (x << SE_CONFIG_DEC_MODE_SHIFT) + +#define SE_RNG_CONFIG_REG_OFFSET 0x340 +#define DRBG_MODE_SHIFT 0 +#define DRBG_MODE_NORMAL 0 +#define DRBG_MODE_FORCE_INSTANTION 1 +#define DRBG_MODE_FORCE_RESEED 2 +#define SE_RNG_CONFIG_MODE(x) (x << DRBG_MODE_SHIFT) + +#define SE_RNG_SRC_CONFIG_REG_OFFSET 0x344 +#define DRBG_RO_ENT_SRC_SHIFT 1 +#define DRBG_RO_ENT_SRC_ENABLE 1 +#define DRBG_RO_ENT_SRC_DISABLE 0 +#define SE_RNG_SRC_CONFIG_RO_ENT_SRC(x) (x << DRBG_RO_ENT_SRC_SHIFT) +#define DRBG_RO_ENT_SRC_LOCK_SHIFT 0 +#define DRBG_RO_ENT_SRC_LOCK_ENABLE 1 +#define DRBG_RO_ENT_SRC_LOCK_DISABLE 0 +#define SE_RNG_SRC_CONFIG_RO_ENT_SRC_LOCK(x) (x << DRBG_RO_ENT_SRC_LOCK_SHIFT) + +#define DRBG_SRC_SHIFT 2 +#define DRBG_SRC_NONE 0 +#define DRBG_SRC_ENTROPY 1 +#define DRBG_SRC_LFSR 2 +#define SE_RNG_CONFIG_SRC(x) (x << DRBG_SRC_SHIFT) + +#define SE_RNG_RESEED_INTERVAL_REG_OFFSET 0x348 + +#define SE_KEYTABLE_REG_OFFSET 0x31c +#define SE_KEYTABLE_SLOT_SHIFT 4 +#define SE_KEYTABLE_SLOT(x) (x << SE_KEYTABLE_SLOT_SHIFT) +#define SE_KEYTABLE_QUAD_SHIFT 2 +#define QUAD_KEYS_128 0 +#define QUAD_KEYS_192 1 +#define QUAD_KEYS_256 1 +#define QUAD_ORG_IV 2 +#define QUAD_UPDTD_IV 3 +#define SE_KEYTABLE_QUAD(x) (x << SE_KEYTABLE_QUAD_SHIFT) +#define SE_KEYTABLE_OP_TYPE_SHIFT 9 +#define OP_READ 0 +#define OP_WRITE 1 +#define SE_KEYTABLE_OP_TYPE(x) (x << SE_KEYTABLE_OP_TYPE_SHIFT) +#define SE_KEYTABLE_TABLE_SEL_SHIFT 8 +#define TABLE_KEYIV 0 +#define TABLE_SCHEDULE 1 +#define SE_KEYTABLE_TABLE_SEL(x) (x << SE_KEYTABLE_TABLE_SEL_SHIFT) +#define SE_KEYTABLE_PKT_SHIFT 0 +#define SE_KEYTABLE_PKT(x) (x << SE_KEYTABLE_PKT_SHIFT) + +#define SE_OP_DONE_SHIFT 4 +#define OP_DONE 1 +#define SE_OP_DONE(x, y) ((x) && (y << SE_OP_DONE_SHIFT)) + +#define SE_CRYPTO_REG_OFFSET 0x304 +#define SE_CRYPTO_HASH_SHIFT 0 +#define HASH_DISABLE 0 +#define HASH_ENABLE 1 +#define SE_CRYPTO_HASH(x) (x << SE_CRYPTO_HASH_SHIFT) +#define SE_CRYPTO_XOR_POS_SHIFT 1 +#define XOR_BYPASS 0 +#define XOR_TOP 2 +#define XOR_BOTTOM 3 +#define SE_CRYPTO_XOR_POS(x) (x << SE_CRYPTO_XOR_POS_SHIFT) +#define SE_CRYPTO_INPUT_SEL_SHIFT 3 +#define INPUT_AHB 0 +#define INPUT_RANDOM 1 +#define INPUT_AESOUT 2 +#define INPUT_LNR_CTR 3 +#define SE_CRYPTO_INPUT_SEL(x) (x << SE_CRYPTO_INPUT_SEL_SHIFT) +#define SE_CRYPTO_VCTRAM_SEL_SHIFT 5 +#define VCTRAM_AHB 0 +#define VCTRAM_AESOUT 2 +#define VCTRAM_PREVAHB 3 +#define SE_CRYPTO_VCTRAM_SEL(x) (x << SE_CRYPTO_VCTRAM_SEL_SHIFT) +#define SE_CRYPTO_IV_SEL_SHIFT 7 +#define IV_ORIGINAL 0 +#define IV_UPDATED 1 +#define SE_CRYPTO_IV_SEL(x) (x << SE_CRYPTO_IV_SEL_SHIFT) +#define SE_CRYPTO_CORE_SEL_SHIFT 8 +#define CORE_DECRYPT 0 +#define CORE_ENCRYPT 1 +#define SE_CRYPTO_CORE_SEL(x) (x << SE_CRYPTO_CORE_SEL_SHIFT) +#define SE_CRYPTO_CTR_VAL_SHIFT 11 +#define SE_CRYPTO_CTR_VAL(x) (x << SE_CRYPTO_CTR_VAL_SHIFT) +#define SE_CRYPTO_KEY_INDEX_SHIFT 24 +#define SE_CRYPTO_KEY_INDEX(x) (x << SE_CRYPTO_KEY_INDEX_SHIFT) +#define SE_CRYPTO_CTR_CNTN_SHIFT 11 +#define SE_CRYPTO_CTR_CNTN(x) (x << SE_CRYPTO_CTR_CNTN_SHIFT) + +#define SE_CRYPTO_CTR_REG_COUNT 4 +#define SE_CRYPTO_CTR_REG_OFFSET 0x308 + +#define SE_OPERATION_REG_OFFSET 0x008 +#define SE_OPERATION_SHIFT 0 +#define OP_ABORT 0 +#define OP_START 1 +#define OP_RESTART 2 +#define OP_CTX_SAVE 3 +#define OP_RESTART_IN 4 +#define SE_OPERATION(x) (x << SE_OPERATION_SHIFT) + +#define SE_CONTEXT_SAVE_CONFIG_REG_OFFSET 0x070 +#define SE_CONTEXT_SAVE_WORD_QUAD_SHIFT 0 +#define KEYS_0_3 0 +#define KEYS_4_7 1 +#define ORIG_IV 2 +#define UPD_IV 3 +#define SE_CONTEXT_SAVE_WORD_QUAD(x) (x << SE_CONTEXT_SAVE_WORD_QUAD_SHIFT) + +#define SE_CONTEXT_SAVE_KEY_INDEX_SHIFT 8 +#define SE_CONTEXT_SAVE_KEY_INDEX(x) (x << SE_CONTEXT_SAVE_KEY_INDEX_SHIFT) + +#define SE_CONTEXT_SAVAE_STICKY_WORD_QUAD_SHIFT 24 +#define STICKY_0_3 0 +#define STICKY_4_7 1 +#define SE_CONTEXT_SAVE_STICKY_WORD_QUAD(x) \ + (x << SE_CONTEXT_SAVAE_STICKY_WORD_QUAD_SHIFT) + +#define SE_CONTEXT_SAVE_SRC_SHIFT 29 +#define STICKY_BITS 0 +#define KEYTABLE 2 +#define MEM 4 +#define SRK 6 + +#define RSA_KEYTABLE 1 +#define SE_CONTEXT_SAVE_SRC(x) (x << SE_CONTEXT_SAVE_SRC_SHIFT) + +#define SE_CONTEXT_SAVE_RSA_KEY_INDEX_SHIFT 16 +#define SE_CONTEXT_SAVE_RSA_KEY_INDEX(x) \ + (x << SE_CONTEXT_SAVE_RSA_KEY_INDEX_SHIFT) + +#define SE_CONTEXT_RSA_WORD_QUAD_SHIFT 12 +#define SE_CONTEXT_RSA_WORD_QUAD(x) \ + (x << SE_CONTEXT_RSA_WORD_QUAD_SHIFT) + +#define SE_INT_ENABLE_REG_OFFSET 0x00c +#define SE_INT_STATUS_REG_OFFSET 0x010 +#define INT_DISABLE 0 +#define INT_ENABLE 1 +#define INT_UNSET 0 +#define INT_SET 1 +#define SE_INT_OP_DONE_SHIFT 4 +#define SE_INT_OP_DONE(x) (x << SE_INT_OP_DONE_SHIFT) +#define SE_INT_ERROR_SHIFT 16 +#define SE_INT_ERROR(x) (x << SE_INT_ERROR_SHIFT) +#define SE_STATUS_0 0x800 +#define SE_ERR_STATUS_0 0x804 + +#define SE_CRYPTO_KEYTABLE_DST_REG_OFFSET 0X330 +#define SE_CRYPTO_KEYTABLE_DST_WORD_QUAD_SHIFT 0 +#define SE_CRYPTO_KEYTABLE_DST_WORD_QUAD(x) \ + (x << SE_CRYPTO_KEYTABLE_DST_WORD_QUAD_SHIFT) + +#define SE_KEY_INDEX_SHIFT 8 +#define SE_CRYPTO_KEYTABLE_DST_KEY_INDEX(x) (x << SE_KEY_INDEX_SHIFT) + +#define SE_IN_LL_ADDR_REG_OFFSET 0x018 +#define SE_OUT_LL_ADDR_REG_OFFSET 0x024 + +#define SE_KEYTABLE_DATA0_REG_OFFSET 0x320 +#define SE_KEYTABLE_REG_MAX_DATA 16 + +#define SE_BLOCK_COUNT_REG_OFFSET 0x318 + +#define SE_SPARE_0_REG_OFFSET 0x80c + +#define SE_SHA_CONFIG_REG_OFFSET 0x200 +#define SHA_DISABLE 0 +#define SHA_ENABLE 1 + +#define SE_SHA_MSG_LENGTH_REG_OFFSET 0x204 +#define SE_SHA_MSG_LEFT_REG_OFFSET 0x214 + +#define SE_HASH_RESULT_REG_COUNT 16 +#define SE_HASH_RESULT_REG_OFFSET 0x030 +#define TEGRA_SE_KEY_256_SIZE 32 +#define TEGRA_SE_KEY_192_SIZE 24 +#define TEGRA_SE_KEY_128_SIZE 16 +#define TEGRA_SE_AES_BLOCK_SIZE 16 +#define TEGRA_SE_AES_MIN_KEY_SIZE 16 +#define TEGRA_SE_AES_MAX_KEY_SIZE 32 +#define TEGRA_SE_AES_IV_SIZE 16 +#define TEGRA_SE_RNG_IV_SIZE 16 +#define TEGRA_SE_RNG_DT_SIZE 16 +#define TEGRA_SE_RNG_KEY_SIZE 16 +#define TEGRA_SE_RNG_SEED_SIZE (TEGRA_SE_RNG_IV_SIZE + \ + TEGRA_SE_RNG_KEY_SIZE + \ + TEGRA_SE_RNG_DT_SIZE) + +#define TEGRA_SE_AES_CMAC_DIGEST_SIZE 16 +#define TEGRA_SE_RSA512_DIGEST_SIZE 64 +#define TEGRA_SE_RSA1024_DIGEST_SIZE 128 +#define TEGRA_SE_RSA1536_DIGEST_SIZE 192 +#define TEGRA_SE_RSA2048_DIGEST_SIZE 256 + +#define SE_KEY_TABLE_ACCESS_LOCK_OFFSET 0x280 +#define SE_KEY_TABLE_ACCESS_REG_OFFSET 0x284 +#define SE_KEY_READ_DISABLE_SHIFT 0 +#define SE_KEY_UPDATE_DISABLE_SHIFT 1 + +#define SE_CONTEXT_BUFER_SIZE 1072 +#define SE_CONTEXT_DRBG_BUFER_SIZE 2112 + +#define SE_CONTEXT_SAVE_RANDOM_DATA_OFFSET 0 +#define SE_CONTEXT_SAVE_RANDOM_DATA_SIZE 16 +#define SE_CONTEXT_SAVE_STICKY_BITS_OFFSET \ + (SE_CONTEXT_SAVE_RANDOM_DATA_OFFSET + SE_CONTEXT_SAVE_RANDOM_DATA_SIZE) +#define SE_CONTEXT_SAVE_STICKY_BITS_SIZE 16 + +#define SE_CONTEXT_SAVE_KEYS_OFFSET (SE_CONTEXT_SAVE_STICKY_BITS_OFFSET + \ + SE_CONTEXT_SAVE_STICKY_BITS_SIZE) +#define SE11_CONTEXT_SAVE_KEYS_OFFSET (SE_CONTEXT_SAVE_STICKY_BITS_OFFSET + \ + SE_CONTEXT_SAVE_STICKY_BITS_SIZE + \ + SE_CONTEXT_SAVE_STICKY_BITS_SIZE) + +#define SE_CONTEXT_SAVE_KEY_LENGTH 512 +#define SE_CONTEXT_ORIGINAL_IV_OFFSET (SE_CONTEXT_SAVE_KEYS_OFFSET + \ + SE_CONTEXT_SAVE_KEY_LENGTH) +#define SE11_CONTEXT_ORIGINAL_IV_OFFSET (SE11_CONTEXT_SAVE_KEYS_OFFSET + \ + SE_CONTEXT_SAVE_KEY_LENGTH) + +#define SE_CONTEXT_ORIGINAL_IV_LENGTH 256 + +#define SE_CONTEXT_UPDATED_IV_OFFSET (SE_CONTEXT_ORIGINAL_IV_OFFSET + \ + SE_CONTEXT_ORIGINAL_IV_LENGTH) +#define SE11_CONTEXT_UPDATED_IV_OFFSET (SE11_CONTEXT_ORIGINAL_IV_OFFSET + \ + SE_CONTEXT_ORIGINAL_IV_LENGTH) + +#define SE_CONTEXT_UPDATED_IV_LENGTH 256 + +#define SE_CONTEXT_SAVE_KNOWN_PATTERN_OFFSET (SE_CONTEXT_UPDATED_IV_OFFSET + \ + SE_CONTEXT_UPDATED_IV_LENGTH) +#define SE11_CONTEXT_SAVE_KNOWN_PATTERN_OFFSET \ + (SE11_CONTEXT_UPDATED_IV_OFFSET + \ + SE_CONTEXT_UPDATED_IV_LENGTH) + +#define SE_CONTEXT_SAVE_RSA_KEYS_OFFSET SE11_CONTEXT_SAVE_KNOWN_PATTERN_OFFSET + +#define SE_CONTEXT_SAVE_RSA_KEY_LENGTH 1024 + +#define SE_CONTEXT_SAVE_RSA_KNOWN_PATTERN_OFFSET \ + (SE_CONTEXT_SAVE_RSA_KEYS_OFFSET + SE_CONTEXT_SAVE_RSA_KEY_LENGTH) + +#define SE_CONTEXT_KNOWN_PATTERN_SIZE 16 + +#define TEGRA_SE_RSA_KEYSLOT_COUNT 2 + +#define SE_RSA_KEYTABLE_ACCESS_LOCK_OFFSET 0x40C +#define SE_RSA_KEYTABLE_ACCESS_REG_OFFSET 0x410 + +#define SE_RSA_KEYTABLE_ADDR 0x420 +#define SE_RSA_KEYTABLE_DATA 0x424 +#define SE_RSA_OUTPUT 0x428 + +#define RSA_KEY_READ 0 +#define RSA_KEY_WRITE 1 +#define SE_RSA_KEY_OP_SHIFT 10 +#define SE_RSA_KEY_OP(x) (x << SE_RSA_KEY_OP_SHIFT) + +#define RSA_KEY_INPUT_MODE_REG 0 +#define RSA_KEY_INPUT_MODE_DMA 1 +#define RSA_KEY_INPUT_MODE_SHIFT 8 +#define RSA_KEY_INPUT_MODE(x) (x << RSA_KEY_INPUT_MODE_SHIFT) + +#define RSA_KEY_SLOT_ONE 0 +#define RSA_KEY_SLOT_TW0 1 +#define RSA_KEY_NUM_SHIFT 7 +#define RSA_KEY_NUM(x) (x << RSA_KEY_NUM_SHIFT) + +#define RSA_KEY_TYPE_EXP 0 +#define RSA_KEY_TYPE_MOD 1 +#define RSA_KEY_TYPE_SHIFT 6 +#define RSA_KEY_TYPE(x) (x << RSA_KEY_TYPE_SHIFT) + +#define SE_RSA_KEY_SIZE_REG_OFFSET 0x404 +#define SE_RSA_EXP_SIZE_REG_OFFSET 0x408 + +#define RSA_KEY_SLOT_SHIFT 24 +#define RSA_KEY_SLOT(x) (x << RSA_KEY_SLOT_SHIFT) +#define SE_RSA_CONFIG 0x400 + +#define RSA_KEY_PKT_WORD_ADDR_SHIFT 0 +#define RSA_KEY_PKT_WORD_ADDR(x) (x << RSA_KEY_PKT_WORD_ADDR_SHIFT) + +#define RSA_KEY_WORD_ADDR_SHIFT 0 +#define RSA_KEY_WORD_ADDR(x) (x << RSA_KEY_WORD_ADDR_SHIFT) + +#define SE_RSA_KEYTABLE_PKT_SHIFT 0 +#define SE_RSA_KEYTABLE_PKT(x) (x << SE_RSA_KEYTABLE_PKT_SHIFT) + +#endif /* _CRYPTO_TEGRA_SE_H */ diff --git a/src/hwinit/t210.h b/src/hwinit/t210.h new file mode 100644 index 0000000..7a965d3 --- /dev/null +++ b/src/hwinit/t210.h @@ -0,0 +1,125 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _T210_H_ +#define _T210_H_ + +#include "types.h" + +#define HOST1X_BASE 0x50000000 +#define DISPLAY_A_BASE 0x54200000 +#define DSI_BASE 0x54300000 +#define VIC_BASE 0x54340000 +#define TSEC_BASE 0x54500000 +#define SOR1_BASE 0x54580000 +#define TMR_BASE 0x60005000 +#define CLOCK_BASE 0x60006000 +#define FLOW_CTLR_BASE 0x60007000 +#define SYSREG_BASE 0x6000C000 +#define SB_BASE (SYSREG_BASE + 0x200) +#define GPIO_BASE 0x6000D000 +#define GPIO_1_BASE (GPIO_BASE) +#define GPIO_2_BASE (GPIO_BASE + 0x100) +#define GPIO_3_BASE (GPIO_BASE + 0x200) +#define GPIO_4_BASE (GPIO_BASE + 0x300) +#define GPIO_5_BASE (GPIO_BASE + 0x400) +#define GPIO_6_BASE (GPIO_BASE + 0x500) +#define GPIO_7_BASE (GPIO_BASE + 0x600) +#define GPIO_8_BASE (GPIO_BASE + 0x700) +#define EXCP_VEC_BASE 0x6000F000 +#define APB_MISC_BASE 0x70000000 +#define PINMUX_AUX_BASE 0x70003000 +#define UART_BASE 0x70006000 +#define PMC_BASE 0x7000E400 +#define SYSCTR0_BASE 0x7000F000 +#define FUSE_BASE 0x7000F800 +#define KFUSE_BASE 0x7000FC00 +#define SE_BASE 0x70012000 +#define MC_BASE 0x70019000 +#define EMC_BASE 0x7001B000 +#define MIPI_CAL_BASE 0x700E3000 +#define I2S_BASE 0x702D1000 + +#define _REG(base, off) *(vu32 *)((base) + (off)) + +#define HOST1X(off) _REG(HOST1X_BASE, off) +#define DISPLAY_A(off) _REG(DISPLAY_A_BASE, off) +#define DSI(off) _REG(DSI_BASE, off) +#define VIC(off) _REG(VIC_BASE, off) +#define TSEC(off) _REG(TSEC_BASE, off) +#define SOR1(off) _REG(SOR1_BASE, off) +#define TMR(off) _REG(TMR_BASE, off) +#define CLOCK(off) _REG(CLOCK_BASE, off) +#define FLOW_CTLR(off) _REG(FLOW_CTLR_BASE, off) +#define SYSREG(off) _REG(SYSREG_BASE, off) +#define SB(off) _REG(SB_BASE, off) +#define GPIO(off) _REG(GPIO_BASE, off) +#define GPIO_1(off) _REG(GPIO_1_BASE, off) +#define GPIO_2(off) _REG(GPIO_2_BASE, off) +#define GPIO_3(off) _REG(GPIO_3_BASE, off) +#define GPIO_4(off) _REG(GPIO_4_BASE, off) +#define GPIO_5(off) _REG(GPIO_5_BASE, off) +#define GPIO_6(off) _REG(GPIO_6_BASE, off) +#define GPIO_7(off) _REG(GPIO_7_BASE, off) +#define GPIO_8(off) _REG(GPIO_8_BASE, off) +#define EXCP_VEC(off) _REG(EXCP_VEC_BASE, off) +#define APB_MISC(off) _REG(APB_MISC_BASE, off) +#define PINMUX_AUX(off) _REG(PINMUX_AUX_BASE, off) +#define PMC(off) _REG(PMC_BASE, off) +#define SYSCTR0(off) _REG(SYSCTR0_BASE, off) +#define FUSE(off) _REG(FUSE_BASE, off) +#define KFUSE(off) _REG(KFUSE_BASE, off) +#define SE(off) _REG(SE_BASE, off) +#define MC(off) _REG(MC_BASE, off) +#define EMC(off) _REG(EMC_BASE, off) +#define MIPI_CAL(off) _REG(MIPI_CAL_BASE, off) +#define I2S(off) _REG(I2S_BASE, off) + +/*! System registers. */ +#define AHB_ARBITRATION_XBAR_CTRL 0xE0 + +/*! Secure boot registers. */ +#define SB_CSR 0x0 +#define SB_AA64_RESET_LOW 0x30 +#define SB_AA64_RESET_HIGH 0x34 + +/*! SYSCTR0 registers. */ +#define SYSCTR0_CNTFID0 0x20 + +/*! Fuse registers. */ +#define FUSE_CTRL 0x0 +#define FUSE_ADDR 0x4 +#define FUSE_RDATA 0x8 +#define FUSE_WDATA 0xC +#define FUSE_TIME_RD1 0x10 +#define FUSE_TIME_RD2 0x14 +#define FUSE_TIME_PGM1 0x18 +#define FUSE_TIME_PGM2 0x1C +#define FUSE_PRIV2INTFC 0x20 +#define FUSE_FUSEBYPASS 0x24 +#define FUSE_PRIVATEKEYDISABLE 0x28 +#define FUSE_DISABLEREGPROGRAM 0x2C +#define FUSE_WRITE_ACCESS_SW 0x30 +#define FUSE_PWR_GOOD_SW 0x34 +#define FUSE_SKU 0x110 +#define FUSE_SOC_SPEEDO_1 0x138 +#define FUSE_SPARE_BIT_5 0x394 + +/*! Fuse cache registers. */ +#define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x)) + + +#endif diff --git a/src/hwinit/tsec.c b/src/hwinit/tsec.c new file mode 100644 index 0000000..612a38a --- /dev/null +++ b/src/hwinit/tsec.c @@ -0,0 +1,136 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "tsec.h" +#include "clock.h" +#include "t210.h" +#include "heap.h" + +static int _tsec_dma_wait_idle() +{ + u32 timeout = TMR(0x10) + 10000000; + + while (!(TSEC(0x1118) & 2)) + if (TMR(0x10) > timeout) + return 0; + + return 1; +} + +static int _tsec_dma_pa_to_internal_100(int not_imem, int i_offset, int pa_offset) +{ + u32 cmd; + + if (not_imem) + cmd = 0x600; // DMA 0x100 bytes + else + cmd = 0x10; // dma imem + + TSEC(0x1114) = i_offset; // tsec_dmatrfmoffs_r + TSEC(0x111C) = pa_offset; // tsec_dmatrffboffs_r + TSEC(0x1118) = cmd; // tsec_dmatrfcmd_r + + return _tsec_dma_wait_idle(); +} + +int tsec_query(u8 *dst, u32 rev, void *fw) +{ + int res = 0; + + //Enable clocks. + clock_enable_host1x(); + clock_enable_tsec(); + clock_enable_sor_safe(); + clock_enable_sor0(); + clock_enable_sor1(); + clock_enable_kfuse(); + + //Configure Falcon. + TSEC(0x110C) = 0; // tsec_dmactl_r + TSEC(0x1010) = 0xFFF2; // tsec_irqmset_r + TSEC(0x101C) = 0xFFF0; // tsec_irqdest_r + TSEC(0x1048) = 3; // tsec_itfen_r + if (!_tsec_dma_wait_idle()) + { + res = -1; + goto out; + } + + //Load firmware. + u8 *fwbuf = (u8 *)malloc(0x2000); + u8 *fwbuf_aligned = (u8 *)ALIGN((u32)fwbuf + 0x1000, 0x100); + memcpy(fwbuf_aligned, fw, 0xF00); + TSEC(0x1110) = (u32)fwbuf_aligned >> 8;// tsec_dmatrfbase_r + for (u32 addr = 0; addr < 0xF00; addr += 0x100) + if (!_tsec_dma_pa_to_internal_100(0, addr, addr)) + { + res = -2; + goto out_free; + } + + //Execute firmware. + HOST1X(0x3300) = 0x34C2E1DA; + TSEC(0x1044) = 0; + TSEC(0x1040) = rev; + TSEC(0x1104) = 0; // tsec_bootvec_r + TSEC(0x1100) = 2; // tsec_cpuctl_r + if (!_tsec_dma_wait_idle()) + { + res = -3; + goto out_free; + } + u32 timeout = TMR(0x10) + 2000000; + while (!TSEC(0x1044)) + if (TMR(0x10) > timeout) + { + res = -4; + goto out_free; + } + if (TSEC(0x1044) != 0xB0B0B0B0) + { + res = -5; + goto out_free; + } + + //Fetch result. + HOST1X(0x3300) = 0; + u32 buf[4]; + buf[0] = SOR1(0x1E8); + buf[1] = SOR1(0x21C); + buf[2] = SOR1(0x208); + buf[3] = SOR1(0x20C); + SOR1(0x1E8) = 0; + SOR1(0x21C) = 0; + SOR1(0x208) = 0; + SOR1(0x20C) = 0; + memcpy(dst, &buf, 0x10); + +out_free:; + free(fwbuf); + +out:; + + //Disable clocks. + clock_disable_kfuse(); + clock_disable_sor1(); + clock_disable_sor0(); + clock_disable_sor_safe(); + clock_disable_tsec(); + clock_disable_host1x(); + + return res; +} diff --git a/src/hwinit/tsec.h b/src/hwinit/tsec.h new file mode 100644 index 0000000..24cce84 --- /dev/null +++ b/src/hwinit/tsec.h @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _TSEC_H_ +#define _TSEC_H_ + +#include "types.h" + +int tsec_query(u8 *dst, u32 rev, void *fw); + +#endif diff --git a/src/hwinit/types.h b/src/hwinit/types.h new file mode 100644 index 0000000..9af580b --- /dev/null +++ b/src/hwinit/types.h @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include +#include + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define OFFSET_OF(t, m) ((u32)&((t *)NULL)->m) +#define CONTAINER_OF(mp, t, mn) ((t *)((u32)mp - OFFSET_OF(t, mn))) + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +typedef volatile uint8_t vu8; +typedef volatile uint16_t vu16; +typedef volatile uint32_t vu32; +typedef volatile uint64_t vu64; + +typedef uintptr_t uPtr; + +enum KB_FIRMWARE_VERSION { + KB_FIRMWARE_VERSION_100_200 = 0, + KB_FIRMWARE_VERSION_300 = 1, + KB_FIRMWARE_VERSION_301 = 2, + KB_FIRMWARE_VERSION_400 = 3, + KB_FIRMWARE_VERSION_500 = 4, + KB_FIRMWARE_VERSION_MAX +}; + +#endif diff --git a/src/hwinit/uart.c b/src/hwinit/uart.c new file mode 100644 index 0000000..2696cf5 --- /dev/null +++ b/src/hwinit/uart.c @@ -0,0 +1,78 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "uart.h" +#include "t210.h" +#include "util.h" + +/* UART A, B, C, D and E. */ +static const u32 uart_baseoff[5] = { 0, 0x40, 0x200, 0x300, 0x400 }; + +void uart_init(u32 idx, u32 baud) +{ + uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + + //Set baud rate. + u32 rate = (8 * baud + 408000000) / (16 * baud); + uart->UART_LCR = 0x80; //Enable DLAB. + uart->UART_THR_DLAB = (u8)rate; //Divisor latch LSB. + uart->UART_IER_DLAB = (u8)(rate >> 8); //Divisor latch MSB. + uart->UART_LCR = 0; //Diable DLAB. + + //Setup UART in fifo mode. + uart->UART_IER_DLAB = 0; + uart->UART_IIR_FCR = 7; //Enable and clear TX and RX FIFOs. + (void)uart->UART_LSR; + sleep(3 * ((baud + 999999) / baud)); + uart->UART_LCR = 3; //Set word length 8. + uart->UART_MCR = 0; + uart->UART_MSR = 0; + uart->UART_IRDA_CSR = 0; + uart->UART_RX_FIFO_CFG = 1; + uart->UART_MIE = 0; + uart->UART_ASR = 0; +} + +void uart_wait_idle(u32 idx, u32 which) +{ + uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + while (!(uart->UART_VENDOR_STATUS & which)) + ; +} + +void uart_send(u32 idx, u8 *buf, u32 len) +{ + uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + + for (u32 i = 0; i != len; i++) + { + while (uart->UART_LSR & UART_TX_FIFO_FULL) + ; + uart->UART_THR_DLAB = buf[i]; + }; +} + +void uart_recv(u32 idx, u8 *buf, u32 len) +{ + uart_t *uart = (uart_t *)(UART_BASE + uart_baseoff[idx]); + + for (u32 i = 0; i != len; i++) + { + while (uart->UART_LSR & UART_RX_FIFO_EMPTY) + ; + buf[i] = uart->UART_THR_DLAB; + }; +} diff --git a/src/hwinit/uart.h b/src/hwinit/uart.h new file mode 100644 index 0000000..845b966 --- /dev/null +++ b/src/hwinit/uart.h @@ -0,0 +1,59 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _UART_H_ +#define _UART_H_ + +#include "types.h" + +#define UART_A 0 +#define UART_B 1 +#define UART_C 2 +//TODO: define clock inits for those. +/*#define UART_D 3 +#define UART_E 4*/ + +#define BAUD_115200 115200 + +#define UART_TX_IDLE 0x00000001 +#define UART_RX_IDLE 0x00000002 +#define UART_TX_FIFO_FULL 0x100 +#define UART_RX_FIFO_EMPTY 0x200 + +typedef struct _uart_t +{ + /* 0x00 */ vu32 UART_THR_DLAB; + /* 0x04 */ vu32 UART_IER_DLAB; + /* 0x08 */ vu32 UART_IIR_FCR; + /* 0x0C */ vu32 UART_LCR; + /* 0x10 */ vu32 UART_MCR; + /* 0x14 */ vu32 UART_LSR; + /* 0x18 */ vu32 UART_MSR; + /* 0x1C */ vu32 UART_SPR; + /* 0x20 */ vu32 UART_IRDA_CSR; + /* 0x24 */ vu32 UART_RX_FIFO_CFG; + /* 0x28 */ vu32 UART_MIE; + /* 0x2C */ vu32 UART_VENDOR_STATUS; + /* 0x30 */ u8 _pad_30[0xC]; + /* 0x3C */ vu32 UART_ASR; +} uart_t; + +void uart_init(u32 idx, u32 baud); +void uart_wait_idle(u32 idx, u32 which); +void uart_send(u32 idx, u8 *buf, u32 len); +void uart_recv(u32 idx, u8 *buf, u32 len); + +#endif diff --git a/src/hwinit/util.c b/src/hwinit/util.c new file mode 100644 index 0000000..9156b3a --- /dev/null +++ b/src/hwinit/util.c @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include +#include "util.h" +#include "t210.h" + +u32 get_tmr() +{ + return TMR(0x10); +} + +void sleep(u32 ticks) +{ + u32 start = TMR(0x10); + while (TMR(0x10) - start <= ticks) + ; +} + +void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops) +{ + for(u32 i = 0; i < num_ops; i++) + base[ops[i].off] = ops[i].val; +} + +uPtr memsearch(const u8 *startPos, u32 searchSize, const void *pattern, u32 patternSize) { + if(!searchSize) return 0; + for (void *pos = startPos; pos <= startPos + searchSize - patternSize; pos++) { + if (memcmp(pos, pattern, patternSize) == 0) return (uPtr)pos; + } + return 0; +} diff --git a/src/hwinit/util.h b/src/hwinit/util.h new file mode 100644 index 0000000..39fa29c --- /dev/null +++ b/src/hwinit/util.h @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include "types.h" + +typedef struct _cfg_op_t +{ + u32 off; + u32 val; +} cfg_op_t; + +u32 get_tmr(); +void sleep(u32 ticks); +void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops); +uPtr memsearch(const u8 *startPos, u32 searchSize, const void *pattern, u32 patternSize); + +#endif diff --git a/src/package.c b/src/package.c new file mode 100644 index 0000000..38c7bb0 --- /dev/null +++ b/src/package.c @@ -0,0 +1,143 @@ +/* +* Copyright (c) 2018 Reisyukaku +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "hwinit/gfx.h" +#include "error.h" +#include "fs.h" +#include "package.h" + +pkg2_hdr_t *unpackFirmwarePackage(u8 *data) { + print("Unpacking firmware...\n"); + pkg2_hdr_t *hdr = (pkg2_hdr_t *)(data + 0x100); + + //Decrypt header. + se_aes_crypt_ctr(8, hdr, sizeof(pkg2_hdr_t), hdr, sizeof(pkg2_hdr_t), hdr); + + if (hdr->magic != PKG2_MAGIC) { + error("Package2 Magic invalid!\n"); + return NULL; + } + + //Decrypt body + data += (0x100 + sizeof(pkg2_hdr_t)); + + for (u32 i = 0; i < 4; i++) { + if (!hdr->sec_size[i]) + continue; + + se_aes_crypt_ctr(8, data, hdr->sec_size[i], data, hdr->sec_size[i], &hdr->sec_ctr[i * 0x10]); + + data += hdr->sec_size[i]; + } + + return hdr; +} + +u8 pkg1_unpack(pk11_offs *offs, u8 *pkg1) { + u8 ret = 0; + u8 *extWb; + u8 *extSec; + + pk11_header *hdr = (pk11_header *)(pkg1 + offs->pkg11_off + 0x20); + + u32 sec_size[3] = { hdr->wb_size, hdr->ldr_size, hdr->sm_size }; + u8 *pdata = (u8 *)hdr + sizeof(pk11_header); + + for (u32 i = 0; i < 3; i++) { + if (offs->sec_map[i] == 0 && offs->warmboot_base) { + u8 *extWb = NULL; + if(fopen("/ReiNX/warmboot.bin", "rb") != NULL) { + extWb = malloc(fsize()); + fread(extWb, fsize(), 1); + fclose(); + } + memcpy((void *)offs->warmboot_base, extWb == NULL ? pdata : extWb, sec_size[offs->sec_map[i]]); + } else if (offs->sec_map[i] == 2 && offs->secmon_base) { + u8 *extSec = NULL; + if(fopen("/ReiNX/secmon.bin", "rb") != NULL) { + extSec = malloc(fsize()); + fread(extSec, fsize(), 1); + fclose(); + } + memcpy((u8 *)offs->secmon_base, extSec == NULL ? pdata : extSec, sec_size[offs->sec_map[i]]); + } + pdata += sec_size[offs->sec_map[i]]; + } + if(extWb != NULL) { + free(extWb); + ret |= 1; + } + if(extSec != NULL) { + free(extSec); + ret |= 2; + } + return ret; +} + +void buildFirmwarePackage(u8 *kernel, u32 kernel_size, link_t *kips_info) { + u8 *pdst = (u8 *)0xA9800000; + + // Signature. + memset(pdst, 0, 0x100); + pdst += 0x100; + + // Header. + pkg2_hdr_t *hdr = (pkg2_hdr_t *)pdst; + memset(hdr, 0, sizeof(pkg2_hdr_t)); + pdst += sizeof(pkg2_hdr_t); + hdr->magic = PKG2_MAGIC; + hdr->base = 0x10000000; + print("kernel @ %08X (%08X)\n", (u32)kernel, kernel_size); + + // Kernel. + u8 *extKern = NULL; + if(fopen("/ReiNX/kernel.bin", "rb") != NULL) { + extKern = malloc(fsize()); + fread(extKern, fsize(), 1); + fclose(); + } + memcpy(pdst, extKern == NULL ? kernel : extKern, kernel_size); + hdr->sec_size[PKG2_SEC_KERNEL] = kernel_size; + hdr->sec_off[PKG2_SEC_KERNEL] = 0x10000000; + se_aes_crypt_ctr(8, pdst, kernel_size, pdst, kernel_size, &hdr->sec_ctr[PKG2_SEC_KERNEL * 0x10]); + pdst += kernel_size; + print("kernel encrypted\n"); + + // INI1. + u32 ini1_size = sizeof(pkg2_ini1_t); + pkg2_ini1_t *ini1 = (pkg2_ini1_t *)pdst; + memset(ini1, 0, sizeof(pkg2_ini1_t)); + ini1->magic = INI1_MAGIC; + pdst += sizeof(pkg2_ini1_t); + LIST_FOREACH_ENTRY(pkg2_kip1_info_t, ki, kips_info, link) { + print("adding kip1 '%s' @ %08X (%08X)\n", ki->kip1->name, (u32)ki->kip1, ki->size); + memcpy(pdst, ki->kip1, ki->size); + pdst += ki->size; + ini1_size += ki->size; + ini1->num_procs++; + } + ini1->size = ini1_size; + hdr->sec_size[PKG2_SEC_INI1] = ini1_size; + hdr->sec_off[PKG2_SEC_INI1] = 0x14080000; + se_aes_crypt_ctr(8, ini1, ini1_size, ini1, ini1_size, &hdr->sec_ctr[PKG2_SEC_INI1 * 0x10]); + print("INI1 encrypted\n"); + + // Encrypt header. + *(u32 *)hdr->ctr = 0x100 + sizeof(pkg2_hdr_t) + kernel_size + ini1_size; + se_aes_crypt_ctr(8, hdr, sizeof(pkg2_hdr_t), hdr, sizeof(pkg2_hdr_t), hdr); + memset(hdr->ctr, 0 , 0x10); + *(u32 *)hdr->ctr = 0x100 + sizeof(pkg2_hdr_t) + kernel_size + ini1_size; +} \ No newline at end of file diff --git a/src/package.h b/src/package.h new file mode 100644 index 0000000..a3dc858 --- /dev/null +++ b/src/package.h @@ -0,0 +1,90 @@ +#pragma once +#include "hwinit.h" + +#define PKG2_MAGIC 0x31324B50 +#define PKG2_SEC_BASE 0x80000000 +#define PKG2_SEC_KERNEL 0 +#define PKG2_SEC_INI1 1 + +#define INI1_MAGIC 0x31494E49 + +typedef struct _pkg2_hdr_t +{ + u8 ctr[0x10]; + u8 sec_ctr[0x40]; + u32 magic; + u32 base; + u32 pad0; + u16 version; + u16 pad1; + u32 sec_size[4]; + u32 sec_off[4]; + u8 sec_sha256[0x80]; + u8 data[]; +} pkg2_hdr_t; + +typedef struct _pkg2_ini1_t +{ + u32 magic; + u32 size; + u32 num_procs; + u32 pad; +} pkg2_ini1_t; + +typedef struct _pkg2_kip1_sec_t +{ + u32 offset; + u32 size_decomp; + u32 size_comp; + u32 attrib; +} pkg2_kip1_sec_t; + +#define KIP1_NUM_SECTIONS 6 + +typedef struct _pkg2_kip1_t +{ + u32 magic; + u8 name[12]; + u64 tid; + u32 proc_cat; + u8 main_thrd_prio; + u8 def_cpu_core; + u8 res; + u8 flags; + pkg2_kip1_sec_t sections[KIP1_NUM_SECTIONS]; + u32 caps[0x20]; + u8 data[]; +} pkg2_kip1_t; + +typedef struct _pkg2_kip1_info_t +{ + pkg2_kip1_t *kip1; + u32 size; + link_t link; +} pkg2_kip1_info_t; + +typedef struct { + const char *id; + u32 kb; + u32 tsec_off; + u32 pkg11_off; + u32 sec_map[3]; + u32 secmon_base; + u32 warmboot_base; + int set_warmboot; +} pk11_offs; + +typedef struct { + u32 magic; + u32 wb_size; + u32 wb_off; + u32 pad; + u32 ldr_size; + u32 ldr_off; + u32 sm_size; + u32 sm_off; +} pk11_header; + +pkg2_hdr_t *unpackFirmwarePackage(u8 *data); +u8 pkg1_unpack(pk11_offs *offs, u8 *pkg1); +void buildFirmwarePackage(u8 *kernel, u32 kernel_size, link_t *kips_info); \ No newline at end of file diff --git a/src/reloc.s b/src/reloc.s new file mode 100644 index 0000000..d89a414 --- /dev/null +++ b/src/reloc.s @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +/* +* TODO: the placement of the relocator is a bit fragile atm, maybe we +* should include it in start.S and copy it to some known good +* place in IRAM instead. Basically we want it as far back atm +* as it might be overwritten during relocation. +*/ + +.section .text.reloc +.arm + +.globl _reloc_ipl +.type _reloc_ipl, %function +_reloc_ipl: + LDMIA R0!, {R4-R7} + STMIA R1!, {R4-R7} + SUBS R2, #0x10 + BNE _reloc_ipl + BX R3 diff --git a/src/start.s b/src/start.s new file mode 100644 index 0000000..995fda0 --- /dev/null +++ b/src/start.s @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +.section .text.start +.arm + +.extern _reloc_ipl +.type _reloc_ipl, %function + +.extern memset +.type memset, %function + +.extern ipl_main +.type ipl_main, %function + +.globl _start +.type _start, %function +_start: + ADR R0, _start + LDR R1, =__ipl_start + CMP R0, R1 + BEQ _real_start + + /* If we are not in the right location already, copy a relocator to upper IRAM. */ + ADR R2, _reloc_ipl + LDR R3, =0x4003FF00 + MOV R4, #(_real_start - _reloc_ipl) +_copy_loop: + LDMIA R2!, {R5} + STMIA R3!, {R5} + SUBS R4, #4 + BNE _copy_loop + + /* Use the relocator to copy ourselves into the right place. */ + LDR R2, =__ipl_end + SUB R2, R2, R1 + LDR R3, =_real_start + LDR R4, =0x4003FF00 + BX R4 + +_reloc_ipl: + LDMIA R0!, {R4-R7} + STMIA R1!, {R4-R7} + SUBS R2, #0x10 + BNE _reloc_ipl + /* Jump to the relocated entry. */ + BX R3 + +_real_start: + /* Initially, we place our stack in IRAM but will move it to SDRAM later. */ + LDR SP, =0x4003FF00 + LDR R0, =__bss_start + EOR R1, R1, R1 + LDR R2, =__bss_end + SUB R2, R2, R0 + BL memset + LDR R0, =0x90020000 + BL heap_init + BL bootrom + BL bootloader + BL firmware + B . + +.globl rebootRCM +.type rebootRCM, %function +rebootRCM: + MOVS R3, #2 + LDR R2, =0x7000E450 + LDR R1, [R2] + ORRS R3, R1 + STR R3, [R2] + MOVS R3, #0x10 + LDR R2, =0x7000E400 + LDR R1, [R2] + ORRS R3, R1 + MOVS R0, #0 + STR R3, [R2] \ No newline at end of file