summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2016-08-26 16:22:19 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2016-08-26 16:22:19 -0500
commit3bac020ef83294fd7c3d40c8ec9c0097da93b876 (patch)
tree865460b93d760a55ac8eecc20d6019d4a1cb6afa
downloadflashrom-raptor-p9-3bac020ef83294fd7c3d40c8ec9c0097da93b876.tar.gz
flashrom-raptor-p9-3bac020ef83294fd7c3d40c8ec9c0097da93b876.zip
Stock SVN revision r1954
-rw-r--r--82802ab.c252
-rw-r--r--COPYING339
-rw-r--r--Makefile1383
-rw-r--r--README173
-rw-r--r--amd_imc.c159
-rw-r--r--archtest.c2
-rw-r--r--at45db.c559
-rw-r--r--atahpt.c103
-rw-r--r--atapromise.c174
-rw-r--r--atavia.c196
-rw-r--r--bitbang_spi.c160
-rw-r--r--board_enable.c2731
-rw-r--r--buspirate_spi.c553
-rw-r--r--cbtable.c304
-rw-r--r--ch341a_spi.c535
-rw-r--r--chipdrivers.h198
-rw-r--r--chipset_enable.c1854
-rw-r--r--cli_classic.c566
-rw-r--r--cli_common.c117
-rw-r--r--cli_output.c102
-rw-r--r--coreboot_tables.h150
-rw-r--r--dediprog.c1090
-rw-r--r--dmi.c484
-rw-r--r--drkaiser.c99
-rw-r--r--dummyflasher.c833
-rw-r--r--en29lv640b.c87
-rw-r--r--flash.h367
-rw-r--r--flashchips.c16092
-rw-r--r--flashchips.h953
-rw-r--r--flashrom.8.tmpl1251
-rw-r--r--flashrom.c2136
-rw-r--r--ft2232_spi.c520
-rw-r--r--gfxnvidia.c125
-rw-r--r--helpers.c103
-rw-r--r--hwaccess.c305
-rw-r--r--hwaccess.h388
-rw-r--r--ich_descriptors.c926
-rw-r--r--ich_descriptors.h598
-rw-r--r--ichspi.c1881
-rw-r--r--internal.c402
-rw-r--r--it8212.c86
-rw-r--r--it85spi.c377
-rw-r--r--it87spi.c438
-rw-r--r--jedec.c758
-rw-r--r--layout.c315
-rw-r--r--linux_spi.c195
-rw-r--r--mcp6x_spi.c168
-rw-r--r--mstarddc_spi.c237
-rw-r--r--nic3com.c148
-rw-r--r--nicintel.c121
-rw-r--r--nicintel_eeprom.c331
-rw-r--r--nicintel_spi.c235
-rw-r--r--nicnatsemi.c115
-rw-r--r--nicrealtek.c138
-rw-r--r--ogp_spi.c147
-rw-r--r--opaque.c66
-rw-r--r--os.h71
-rw-r--r--pcidev.c333
-rw-r--r--physmap.c682
-rw-r--r--pickit2_spi.c509
-rw-r--r--platform.h72
-rw-r--r--pony_spi.c236
-rw-r--r--print.c1180
-rw-r--r--print_wiki.c457
-rw-r--r--processor_enable.c88
-rw-r--r--programmer.c144
-rw-r--r--programmer.h782
-rw-r--r--rayer_spi.c290
-rw-r--r--satamv.c197
-rw-r--r--satasii.c141
-rw-r--r--sb600spi.c700
-rw-r--r--serial.c597
-rw-r--r--serprog.c969
-rw-r--r--serprog.h25
-rw-r--r--sfdp.c394
-rw-r--r--spi.c186
-rw-r--r--spi.h163
-rw-r--r--spi25.c1173
-rw-r--r--spi25_statusreg.c738
-rw-r--r--sst28sf040.c127
-rw-r--r--sst49lfxxxc.c40
-rw-r--r--sst_fwhub.c95
-rw-r--r--stm50.c60
-rw-r--r--udelay.c209
-rw-r--r--usbblaster_spi.c225
-rw-r--r--w29ee011.c72
-rw-r--r--w39.c243
-rw-r--r--wbsio_spi.c211
88 files changed, 54534 insertions, 0 deletions
diff --git a/82802ab.c b/82802ab.c
new file mode 100644
index 0000000..1436f8a
--- /dev/null
+++ b/82802ab.c
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Datasheet:
+ * - Name: Intel 82802AB/82802AC Firmware Hub (FWH)
+ * - URL: http://www.intel.com/design/chipsets/datashts/290658.htm
+ * - PDF: http://download.intel.com/design/chipsets/datashts/29065804.pdf
+ * - Order number: 290658-004
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+void print_status_82802ab(uint8_t status)
+{
+ msg_cdbg("%s", status & 0x80 ? "Ready:" : "Busy:");
+ msg_cdbg("%s", status & 0x40 ? "BE SUSPEND:" : "BE RUN/FINISH:");
+ msg_cdbg("%s", status & 0x20 ? "BE ERROR:" : "BE OK:");
+ msg_cdbg("%s", status & 0x10 ? "PROG ERR:" : "PROG OK:");
+ msg_cdbg("%s", status & 0x8 ? "VP ERR:" : "VPP OK:");
+ msg_cdbg("%s", status & 0x4 ? "PROG SUSPEND:" : "PROG RUN/FINISH:");
+ msg_cdbg("%s", status & 0x2 ? "WP|TBL#|WP#,ABORT:" : "UNLOCK:");
+}
+
+int probe_82802ab(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t id1, id2, flashcontent1, flashcontent2;
+ int shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED) ? 1 : 0;
+
+ /* Reset to get a clean state */
+ chip_writeb(flash, 0xFF, bios);
+ programmer_delay(10);
+
+ /* Enter ID mode */
+ chip_writeb(flash, 0x90, bios);
+ programmer_delay(10);
+
+ id1 = chip_readb(flash, bios + (0x00 << shifted));
+ id2 = chip_readb(flash, bios + (0x01 << shifted));
+
+ /* Leave ID mode */
+ chip_writeb(flash, 0xFF, bios);
+
+ programmer_delay(10);
+
+ msg_cdbg("%s: id1 0x%02x, id2 0x%02x", __func__, id1, id2);
+
+ if (!oddparity(id1))
+ msg_cdbg(", id1 parity violation");
+
+ /*
+ * Read the product ID location again. We should now see normal
+ * flash contents.
+ */
+ flashcontent1 = chip_readb(flash, bios + (0x00 << shifted));
+ flashcontent2 = chip_readb(flash, bios + (0x01 << shifted));
+
+ if (id1 == flashcontent1)
+ msg_cdbg(", id1 is normal flash content");
+ if (id2 == flashcontent2)
+ msg_cdbg(", id2 is normal flash content");
+
+ msg_cdbg("\n");
+ if (id1 != flash->chip->manufacture_id || id2 != flash->chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+/* FIXME: needs timeout */
+uint8_t wait_82802ab(struct flashctx *flash)
+{
+ uint8_t status;
+ chipaddr bios = flash->virtual_memory;
+
+ chip_writeb(flash, 0x70, bios);
+ if ((chip_readb(flash, bios) & 0x80) == 0) { // it's busy
+ while ((chip_readb(flash, bios) & 0x80) == 0) ;
+ }
+
+ status = chip_readb(flash, bios);
+
+ /* Reset to get a clean state */
+ chip_writeb(flash, 0xFF, bios);
+
+ return status;
+}
+
+int erase_block_82802ab(struct flashctx *flash, unsigned int page,
+ unsigned int pagesize)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t status;
+
+ // clear status register
+ chip_writeb(flash, 0x50, bios + page);
+
+ // now start it
+ chip_writeb(flash, 0x20, bios + page);
+ chip_writeb(flash, 0xd0, bios + page);
+ programmer_delay(10);
+
+ // now let's see what the register is
+ status = wait_82802ab(flash);
+ print_status_82802ab(status);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* chunksize is 1 */
+int write_82802ab(struct flashctx *flash, const uint8_t *src, unsigned int start, unsigned int len)
+{
+ int i;
+ chipaddr dst = flash->virtual_memory + start;
+
+ for (i = 0; i < len; i++) {
+ /* transfer data from source to destination */
+ chip_writeb(flash, 0x40, dst);
+ chip_writeb(flash, *src++, dst++);
+ wait_82802ab(flash);
+ }
+
+ /* FIXME: Ignore errors for now. */
+ return 0;
+}
+
+int unlock_28f004s5(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t mcfg, bcfg, need_unlock = 0, can_unlock = 0;
+ int i;
+
+ /* Clear status register */
+ chip_writeb(flash, 0x50, bios);
+
+ /* Read identifier codes */
+ chip_writeb(flash, 0x90, bios);
+
+ /* Read master lock-bit */
+ mcfg = chip_readb(flash, bios + 0x3);
+ msg_cdbg("master lock is ");
+ if (mcfg) {
+ msg_cdbg("locked!\n");
+ } else {
+ msg_cdbg("unlocked!\n");
+ can_unlock = 1;
+ }
+
+ /* Read block lock-bits */
+ for (i = 0; i < flash->chip->total_size * 1024; i+= (64 * 1024)) {
+ bcfg = chip_readb(flash, bios + i + 2); // read block lock config
+ msg_cdbg("block lock at %06x is %slocked!\n", i, bcfg ? "" : "un");
+ if (bcfg) {
+ need_unlock = 1;
+ }
+ }
+
+ /* Reset chip */
+ chip_writeb(flash, 0xFF, bios);
+
+ /* Unlock: clear block lock-bits, if needed */
+ if (can_unlock && need_unlock) {
+ msg_cdbg("Unlock: ");
+ chip_writeb(flash, 0x60, bios);
+ chip_writeb(flash, 0xD0, bios);
+ chip_writeb(flash, 0xFF, bios);
+ msg_cdbg("Done!\n");
+ }
+
+ /* Error: master locked or a block is locked */
+ if (!can_unlock && need_unlock) {
+ msg_cerr("At least one block is locked and lockdown is active!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int unlock_lh28f008bjt(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t mcfg, bcfg;
+ uint8_t need_unlock = 0, can_unlock = 0;
+ int i;
+
+ /* Wait if chip is busy */
+ wait_82802ab(flash);
+
+ /* Read identifier codes */
+ chip_writeb(flash, 0x90, bios);
+
+ /* Read master lock-bit */
+ mcfg = chip_readb(flash, bios + 0x3);
+ msg_cdbg("master lock is ");
+ if (mcfg) {
+ msg_cdbg("locked!\n");
+ } else {
+ msg_cdbg("unlocked!\n");
+ can_unlock = 1;
+ }
+
+ /* Read block lock-bits, 8 * 8 KB + 15 * 64 KB */
+ for (i = 0; i < flash->chip->total_size * 1024;
+ i += (i >= (64 * 1024) ? 64 * 1024 : 8 * 1024)) {
+ bcfg = chip_readb(flash, bios + i + 2); /* read block lock config */
+ msg_cdbg("block lock at %06x is %slocked!\n", i,
+ bcfg ? "" : "un");
+ if (bcfg)
+ need_unlock = 1;
+ }
+
+ /* Reset chip */
+ chip_writeb(flash, 0xFF, bios);
+
+ /* Unlock: clear block lock-bits, if needed */
+ if (can_unlock && need_unlock) {
+ msg_cdbg("Unlock: ");
+ chip_writeb(flash, 0x60, bios);
+ chip_writeb(flash, 0xD0, bios);
+ chip_writeb(flash, 0xFF, bios);
+ wait_82802ab(flash);
+ msg_cdbg("Done!\n");
+ }
+
+ /* Error: master locked or a block is locked */
+ if (!can_unlock && need_unlock) {
+ msg_cerr("At least one block is locked and lockdown is active!\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/COPYING
@@ -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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4ebde1e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,1383 @@
+#
+# This file is part of the flashrom project.
+#
+# Copyright (C) 2005 coresystems GmbH <stepan@coresystems.de>
+# Copyright (C) 2009,2010,2012 Carl-Daniel Hailfinger
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+PROGRAM = flashrom
+
+###############################################################################
+# Defaults for the toolchain.
+
+# If you want to cross-compile, just run e.g.
+# make CC=i586-pc-msdosdjgpp-gcc
+# You may have to specify STRIP/AR/RANLIB as well.
+#
+# Note for anyone editing this Makefile: gnumake will happily ignore any
+# changes in this Makefile to variables set on the command line.
+STRIP ?= strip
+INSTALL = install
+DIFF = diff
+PREFIX ?= /usr/local
+MANDIR ?= $(PREFIX)/share/man
+CFLAGS ?= -Os -Wall -Wshadow
+EXPORTDIR ?= .
+RANLIB ?= ranlib
+PKG_CONFIG ?= pkg-config
+BUILD_DETAILS_FILE ?= build_details.txt
+
+# The following parameter changes the default programmer that will be used if there is no -p/--programmer
+# argument given when running flashrom. The predefined setting does not enable any default so that every
+# user has to declare the programmer he wants to use on every run. The rationale for this to be not set
+# (to e.g. the internal programmer) is that forgetting to specify this when working with another programmer
+# easily puts the system attached to the default programmer at risk (e.g. you want to flash coreboot to another
+# system attached to an external programmer while the default programmer is set to the internal programmer, and
+# you forget to use the -p parameter. This would (try to) overwrite the existing firmware of the computer
+# running flashrom). Please do not enable this without thinking about the possible consequences. Possible
+# values are those specified in enum programmer in programmer.h (which depend on other CONFIG_* options
+# evaluated below, namely those that enable/disable the various programmers).
+# Compilation will fail for unspecified values.
+CONFIG_DEFAULT_PROGRAMMER ?= PROGRAMMER_INVALID
+# The following adds a default parameter for the default programmer set above (only).
+CONFIG_DEFAULT_PROGRAMMER_ARGS ?= ''
+# Example: compiling with
+# make CONFIG_DEFAULT_PROGRAMMER=PROGRAMMER_SERPROG CONFIG_DEFAULT_PROGRAMMER_ARGS="dev=/dev/ttyUSB0:1500000"
+# would make executing './flashrom' (almost) equivialent to './flashrom -p serprog:dev=/dev/ttyUSB0:1500000'.
+
+# If your compiler spits out excessive warnings, run make WARNERROR=no
+# You shouldn't have to change this flag.
+WARNERROR ?= yes
+
+ifeq ($(WARNERROR), yes)
+CFLAGS += -Werror
+endif
+
+ifdef LIBS_BASE
+PKG_CONFIG_LIBDIR ?= $(LIBS_BASE)/lib/pkgconfig
+override CPPFLAGS += -I$(LIBS_BASE)/include
+override LDFLAGS += -L$(LIBS_BASE)/lib -Wl,-rpath -Wl,$(LIBS_BASE)/lib
+endif
+
+ifeq ($(CONFIG_STATIC),yes)
+override PKG_CONFIG += --static
+override LDFLAGS += -static
+endif
+
+# Set LC_ALL=C to minimize influences of the locale.
+# However, this won't work for the majority of relevant commands because they use the $(shell) function and
+# GNU make does not relay variables exported within the makefile to their evironment.
+LC_ALL=C
+export LC_ALL
+
+dummy_for_make_3_80:=$(shell printf "Build started on %s\n\n" "$$(date)" >$(BUILD_DETAILS_FILE))
+
+# Provide an easy way to execute a command, print its output to stdout and capture any error message on stderr
+# in the build details file together with the original stdout output.
+debug_shell = $(shell export LC_ALL=C ; { echo 'exec: export LC_ALL=C ; { $(1) ; }' >&2; { $(1) ; } | tee -a $(BUILD_DETAILS_FILE) ; echo >&2 ; } 2>>$(BUILD_DETAILS_FILE))
+
+###############################################################################
+# General OS-specific settings.
+# 1. Prepare for later by gathering information about host and target OS
+# 2. Set compiler flags and parameters according to OSes
+# 3. Likewise verify user-supplied CONFIG_* variables.
+
+# HOST_OS is only used to work around local toolchain issues.
+HOST_OS ?= $(shell uname)
+ifeq ($(HOST_OS), MINGW32_NT-5.1)
+# Explicitly set CC = gcc on MinGW, otherwise: "cc: command not found".
+CC = gcc
+endif
+ifneq ($(HOST_OS), SunOS)
+STRIP_ARGS = -s
+endif
+
+# Determine the destination OS.
+# IMPORTANT: The following line must be placed before TARGET_OS is ever used
+# (of course), but should come after any lines setting CC because the line
+# below uses CC itself.
+override TARGET_OS := $(strip $(call debug_shell,$(CC) $(CPPFLAGS) -E os.h 2>/dev/null | grep -v '^\#' | grep '"' | cut -f 2 -d'"'))
+
+ifeq ($(TARGET_OS), Darwin)
+override CPPFLAGS += -I/opt/local/include -I/usr/local/include
+override LDFLAGS += -L/opt/local/lib -L/usr/local/lib
+endif
+
+ifeq ($(TARGET_OS), FreeBSD)
+override CPPFLAGS += -I/usr/local/include
+override LDFLAGS += -L/usr/local/lib
+endif
+
+ifeq ($(TARGET_OS), OpenBSD)
+override CPPFLAGS += -I/usr/local/include
+override LDFLAGS += -L/usr/local/lib
+endif
+
+ifeq ($(TARGET_OS), NetBSD)
+override CPPFLAGS += -I/usr/pkg/include
+override LDFLAGS += -L/usr/pkg/lib
+endif
+
+ifeq ($(TARGET_OS), DragonFlyBSD)
+override CPPFLAGS += -I/usr/local/include
+override LDFLAGS += -L/usr/local/lib
+endif
+
+ifeq ($(TARGET_OS), DOS)
+EXEC_SUFFIX := .exe
+# DJGPP has odd uint*_t definitions which cause lots of format string warnings.
+override CFLAGS += -Wno-format
+LIBS += -lgetopt
+# Bus Pirate, Serprog and PonyProg are not supported under DOS (missing serial support).
+ifeq ($(CONFIG_BUSPIRATE_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_BUSPIRATE_SPI=yes
+else
+override CONFIG_BUSPIRATE_SPI = no
+endif
+ifeq ($(CONFIG_SERPROG), yes)
+UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
+else
+override CONFIG_SERPROG = no
+endif
+ifeq ($(CONFIG_PONY_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes
+else
+override CONFIG_PONY_SPI = no
+endif
+# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported under DOS (missing USB support).
+ifeq ($(CONFIG_DEDIPROG), yes)
+UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes
+else
+override CONFIG_DEDIPROG = no
+endif
+ifeq ($(CONFIG_FT2232_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes
+else
+override CONFIG_FT2232_SPI = no
+endif
+ifeq ($(CONFIG_USBBLASTER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_USBBLASTER_SPI=yes
+else
+override CONFIG_USBBLASTER_SPI = no
+endif
+ifeq ($(CONFIG_PICKIT2_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes
+else
+override CONFIG_PICKIT2_SPI = no
+endif
+ifeq ($(CONFIG_CH341A_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
+else
+override CONFIG_CH341A_SPI = no
+endif
+endif
+
+# FIXME: Should we check for Cygwin/MSVC as well?
+ifeq ($(TARGET_OS), MinGW)
+EXEC_SUFFIX := .exe
+# MinGW doesn't have the ffs() function, but we can use gcc's __builtin_ffs().
+FLASHROM_CFLAGS += -Dffs=__builtin_ffs
+# Some functions provided by Microsoft do not work as described in C99 specifications. This macro fixes that
+# for MinGW. See http://sourceforge.net/p/mingw-w64/wiki2/printf%20and%20scanf%20family/ */
+FLASHROM_CFLAGS += -D__USE_MINGW_ANSI_STDIO=1
+# For now we disable all PCI-based programmers on Windows/MinGW (no libpci).
+ifeq ($(CONFIG_INTERNAL), yes)
+UNSUPPORTED_FEATURES += CONFIG_INTERNAL=yes
+else
+override CONFIG_INTERNAL = no
+endif
+ifeq ($(CONFIG_RAYER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAYER_SPI=yes
+else
+override CONFIG_RAYER_SPI = no
+endif
+ifeq ($(CONFIG_NIC3COM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NIC3COM=yes
+else
+override CONFIG_NIC3COM = no
+endif
+ifeq ($(CONFIG_GFXNVIDIA), yes)
+UNSUPPORTED_FEATURES += CONFIG_GFXNVIDIA=yes
+else
+override CONFIG_GFXNVIDIA = no
+endif
+ifeq ($(CONFIG_SATASII), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATASII=yes
+else
+override CONFIG_SATASII = no
+endif
+ifeq ($(CONFIG_ATAHPT), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAHPT=yes
+else
+override CONFIG_ATAHPT = no
+endif
+ifeq ($(CONFIG_ATAVIA), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAVIA=yes
+else
+override CONFIG_ATAVIA = no
+endif
+ifeq ($(CONFIG_ATAPROMISE), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAPROMISE=yes
+else
+override CONFIG_ATAPROMISE = no
+endif
+ifeq ($(CONFIG_IT8212), yes)
+UNSUPPORTED_FEATURES += CONFIG_IT8212=yes
+else
+override CONFIG_IT8212 = no
+endif
+ifeq ($(CONFIG_DRKAISER), yes)
+UNSUPPORTED_FEATURES += CONFIG_DRKAISER=yes
+else
+override CONFIG_DRKAISER = no
+endif
+ifeq ($(CONFIG_NICREALTEK), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICREALTEK=yes
+else
+override CONFIG_NICREALTEK = no
+endif
+ifeq ($(CONFIG_NICNATSEMI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICNATSEMI=yes
+else
+override CONFIG_NICNATSEMI = no
+endif
+ifeq ($(CONFIG_NICINTEL), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL=yes
+else
+override CONFIG_NICINTEL = no
+endif
+ifeq ($(CONFIG_NICINTEL_EEPROM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL_EEPROM=yes
+else
+override CONFIG_NICINTEL_EEPROM = no
+endif
+ifeq ($(CONFIG_NICINTEL_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL_SPI=yes
+else
+override CONFIG_NICINTEL_SPI = no
+endif
+ifeq ($(CONFIG_OGP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_OGP_SPI=yes
+else
+override CONFIG_OGP_SPI = no
+endif
+ifeq ($(CONFIG_SATAMV), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATAMV=yes
+else
+override CONFIG_SATAMV = no
+endif
+endif
+
+ifeq ($(TARGET_OS), libpayload)
+ifeq ($(MAKECMDGOALS),)
+.DEFAULT_GOAL := libflashrom.a
+$(info Setting default goal to libflashrom.a)
+endif
+FLASHROM_CFLAGS += -DSTANDALONE
+ifeq ($(CONFIG_DUMMY), yes)
+UNSUPPORTED_FEATURES += CONFIG_DUMMY=yes
+else
+override CONFIG_DUMMY = no
+endif
+# libpayload does not provide the romsize field in struct pci_dev that the atapromise code requires.
+ifeq ($(CONFIG_ATAPROMISE), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAPROMISE=yes
+else
+override CONFIG_ATAPROMISE = no
+endif
+# Bus Pirate, Serprog and PonyProg are not supported with libpayload (missing serial support).
+ifeq ($(CONFIG_BUSPIRATE_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_BUSPIRATE_SPI=yes
+else
+override CONFIG_BUSPIRATE_SPI = no
+endif
+ifeq ($(CONFIG_SERPROG), yes)
+UNSUPPORTED_FEATURES += CONFIG_SERPROG=yes
+else
+override CONFIG_SERPROG = no
+endif
+ifeq ($(CONFIG_PONY_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_PONY_SPI=yes
+else
+override CONFIG_PONY_SPI = no
+endif
+# Dediprog, USB-Blaster, PICkit2, CH341A and FT2232 are not supported with libpayload (missing libusb support).
+ifeq ($(CONFIG_DEDIPROG), yes)
+UNSUPPORTED_FEATURES += CONFIG_DEDIPROG=yes
+else
+override CONFIG_DEDIPROG = no
+endif
+ifeq ($(CONFIG_FT2232_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_FT2232_SPI=yes
+else
+override CONFIG_FT2232_SPI = no
+endif
+ifeq ($(CONFIG_USBBLASTER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_USBBLASTER_SPI=yes
+else
+override CONFIG_USBBLASTER_SPI = no
+endif
+ifeq ($(CONFIG_PICKIT2_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_PICKIT2_SPI=yes
+else
+override CONFIG_PICKIT2_SPI = no
+endif
+ifeq ($(CONFIG_CH341A_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_CH341A_SPI=yes
+else
+override CONFIG_CH341A_SPI = no
+endif
+endif
+
+ifneq ($(TARGET_OS), Linux)
+# Android is handled internally as separate OS, but it supports CONFIG_LINUX_SPI and CONFIG_MSTARDDC_SPI
+ifneq ($(TARGET_OS), Android)
+ifeq ($(CONFIG_LINUX_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_LINUX_SPI=yes
+else
+override CONFIG_LINUX_SPI = no
+endif
+ifeq ($(CONFIG_MSTARDDC_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_MSTARDDC_SPI=yes
+else
+override CONFIG_MSTARDDC_SPI = no
+endif
+endif
+endif
+
+ifeq ($(TARGET_OS), Android)
+# Android on x86 (currently) does not provide raw PCI port I/O operations
+ifeq ($(CONFIG_RAYER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAYER_SPI=yes
+else
+override CONFIG_RAYER_SPI = no
+endif
+endif
+
+###############################################################################
+# General architecture-specific settings.
+# Like above for the OS, below we verify user-supplied options depending on the target architecture.
+
+# Determine the destination processor architecture.
+# IMPORTANT: The following line must be placed before ARCH is ever used
+# (of course), but should come after any lines setting CC because the line
+# below uses CC itself.
+override ARCH := $(strip $(call debug_shell,$(CC) $(CPPFLAGS) -E archtest.c 2>/dev/null | grep -v '^\#' | grep '"' | cut -f 2 -d'"'))
+
+# PCI port I/O support is unimplemented on PPC/MIPS/SPARC and unavailable on ARM.
+# Right now this means the drivers below only work on x86.
+ifneq ($(ARCH), x86)
+ifeq ($(CONFIG_NIC3COM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NIC3COM=yes
+else
+override CONFIG_NIC3COM = no
+endif
+ifeq ($(CONFIG_NICREALTEK), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICREALTEK=yes
+else
+override CONFIG_NICREALTEK = no
+endif
+ifeq ($(CONFIG_NICNATSEMI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICNATSEMI=yes
+else
+override CONFIG_NICNATSEMI = no
+endif
+ifeq ($(CONFIG_RAYER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAYER_SPI=yes
+else
+override CONFIG_RAYER_SPI = no
+endif
+ifeq ($(CONFIG_ATAHPT), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAHPT=yes
+else
+override CONFIG_ATAHPT = no
+endif
+ifeq ($(CONFIG_ATAPROMISE), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAPROMISE=yes
+else
+override CONFIG_ATAPROMISE = no
+endif
+ifeq ($(CONFIG_SATAMV), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATAMV=yes
+else
+override CONFIG_SATAMV = no
+endif
+endif
+
+# Disable all drivers needing raw access (memory, PCI, port I/O) on
+# architectures with unknown raw access properties.
+# Right now those architectures are alpha hppa m68k sh s390
+ifneq ($(ARCH),$(filter $(ARCH),x86 mips ppc arm sparc))
+ifeq ($(CONFIG_INTERNAL), yes)
+UNSUPPORTED_FEATURES += CONFIG_INTERNAL=yes
+else
+override CONFIG_INTERNAL = no
+endif
+ifeq ($(CONFIG_RAYER_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_RAYER_SPI=yes
+else
+override CONFIG_RAYER_SPI = no
+endif
+ifeq ($(CONFIG_NIC3COM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NIC3COM=yes
+else
+override CONFIG_NIC3COM = no
+endif
+ifeq ($(CONFIG_GFXNVIDIA), yes)
+UNSUPPORTED_FEATURES += CONFIG_GFXNVIDIA=yes
+else
+override CONFIG_GFXNVIDIA = no
+endif
+ifeq ($(CONFIG_SATASII), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATASII=yes
+else
+override CONFIG_SATASII = no
+endif
+ifeq ($(CONFIG_ATAHPT), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAHPT=yes
+else
+override CONFIG_ATAHPT = no
+endif
+ifeq ($(CONFIG_ATAVIA), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAVIA=yes
+else
+override CONFIG_ATAVIA = no
+endif
+ifeq ($(CONFIG_ATAPROMISE), yes)
+UNSUPPORTED_FEATURES += CONFIG_ATAPROMISE=yes
+else
+override CONFIG_ATAPROMISE = no
+endif
+ifeq ($(CONFIG_DRKAISER), yes)
+UNSUPPORTED_FEATURES += CONFIG_DRKAISER=yes
+else
+override CONFIG_DRKAISER = no
+endif
+ifeq ($(CONFIG_NICREALTEK), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICREALTEK=yes
+else
+override CONFIG_NICREALTEK = no
+endif
+ifeq ($(CONFIG_NICNATSEMI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICNATSEMI=yes
+else
+override CONFIG_NICNATSEMI = no
+endif
+ifeq ($(CONFIG_NICINTEL), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL=yes
+else
+override CONFIG_NICINTEL = no
+endif
+ifeq ($(CONFIG_NICINTEL_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL_SPI=yes
+else
+override CONFIG_NICINTEL_SPI = no
+endif
+ifeq ($(CONFIG_NICINTEL_EEPROM), yes)
+UNSUPPORTED_FEATURES += CONFIG_NICINTEL_EEPROM=yes
+else
+override CONFIG_NICINTEL_EEPROM = no
+endif
+ifeq ($(CONFIG_OGP_SPI), yes)
+UNSUPPORTED_FEATURES += CONFIG_OGP_SPI=yes
+else
+override CONFIG_OGP_SPI = no
+endif
+ifeq ($(CONFIG_SATAMV), yes)
+UNSUPPORTED_FEATURES += CONFIG_SATAMV=yes
+else
+override CONFIG_SATAMV = no
+endif
+ifeq ($(CONFIG_IT8212), yes)
+UNSUPPORTED_FEATURES += CONFIG_IT8212=yes
+else
+override CONFIG_IT8212 = no
+endif
+endif
+
+###############################################################################
+# Flash chip drivers and bus support infrastructure.
+
+CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
+ sst28sf040.o 82802ab.o \
+ sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o spi25_statusreg.o \
+ opaque.o sfdp.o en29lv640b.o at45db.o
+
+###############################################################################
+# Library code.
+
+LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o
+
+###############################################################################
+# Frontend related stuff.
+
+CLI_OBJS = cli_classic.o cli_output.o cli_common.o print.o
+
+# Set the flashrom version string from the highest revision number of the checked out flashrom files.
+# Note to packagers: Any tree exported with "make export" or "make tarball"
+# will not require subversion. The downloadable snapshots are already exported.
+SVNVERSION := $(shell ./util/getrevision.sh -u 2>/dev/null )
+
+RELEASE := 0.9.9
+VERSION := $(RELEASE)-$(SVNVERSION)
+RELEASENAME ?= $(VERSION)
+
+SVNDEF := -D'FLASHROM_VERSION="$(VERSION)"'
+
+# Inform user if there is no meaningful version string. If there is version information from a VCS print
+# something anyway because $(info...) will print a line break in any case which would look suspicious.
+# The && between the echos is a workaround for old versions of GNU make that issue the error "unterminated
+# variable reference" if a semicolon is used instead.
+$(info $(shell ./util/getrevision.sh -c 2>/dev/null || echo "Files don't seem to be under version control." && \
+ echo "Replacing all version templates with $(VERSION)." ))
+
+###############################################################################
+# Default settings of CONFIG_* variables.
+
+# Always enable internal/onboard support for now.
+CONFIG_INTERNAL ?= yes
+
+# Always enable serprog for now.
+CONFIG_SERPROG ?= yes
+
+# RayeR SPIPGM hardware support
+CONFIG_RAYER_SPI ?= yes
+
+# PonyProg2000 SPI hardware support
+CONFIG_PONY_SPI ?= yes
+
+# Always enable 3Com NICs for now.
+CONFIG_NIC3COM ?= yes
+
+# Enable NVIDIA graphics cards. Note: write and erase do not work properly.
+CONFIG_GFXNVIDIA ?= yes
+
+# Always enable SiI SATA controllers for now.
+CONFIG_SATASII ?= yes
+
+# Highpoint (HPT) ATA/RAID controller support.
+# IMPORTANT: This code is not yet working!
+CONFIG_ATAHPT ?= no
+
+# VIA VT6421A LPC memory support
+CONFIG_ATAVIA ?= yes
+
+# Promise ATA controller support.
+CONFIG_ATAPROMISE ?= no
+
+# Always enable FT2232 SPI dongles for now.
+CONFIG_FT2232_SPI ?= yes
+
+# Always enable Altera USB-Blaster dongles for now.
+CONFIG_USBBLASTER_SPI ?= yes
+
+# MSTAR DDC support needs more tests/reviews/cleanups.
+CONFIG_MSTARDDC_SPI ?= no
+
+# Always enable PICkit2 SPI dongles for now.
+CONFIG_PICKIT2_SPI ?= yes
+
+# Always enable dummy tracing for now.
+CONFIG_DUMMY ?= yes
+
+# Always enable Dr. Kaiser for now.
+CONFIG_DRKAISER ?= yes
+
+# Always enable Realtek NICs for now.
+CONFIG_NICREALTEK ?= yes
+
+# Disable National Semiconductor NICs until support is complete and tested.
+CONFIG_NICNATSEMI ?= no
+
+# Always enable Intel NICs for now.
+CONFIG_NICINTEL ?= yes
+
+# Always enable SPI on Intel NICs for now.
+CONFIG_NICINTEL_SPI ?= yes
+
+# Always enable EEPROM on Intel NICs for now.
+CONFIG_NICINTEL_EEPROM ?= yes
+
+# Always enable SPI on OGP cards for now.
+CONFIG_OGP_SPI ?= yes
+
+# Always enable Bus Pirate SPI for now.
+CONFIG_BUSPIRATE_SPI ?= yes
+
+# Always enable Dediprog SF100 for now.
+CONFIG_DEDIPROG ?= yes
+
+# Always enable Marvell SATA controllers for now.
+CONFIG_SATAMV ?= yes
+
+# Enable Linux spidev interface by default. We disable it on non-Linux targets.
+CONFIG_LINUX_SPI ?= yes
+
+# Always enable ITE IT8212F PATA controllers for now.
+CONFIG_IT8212 ?= yes
+
+# Winchiphead CH341A
+CONFIG_CH341A_SPI ?= yes
+
+# Disable wiki printing by default. It is only useful if you have wiki access.
+CONFIG_PRINT_WIKI ?= no
+
+# Disable all features if CONFIG_NOTHING=yes is given unless CONFIG_EVERYTHING was also set
+ifeq ($(CONFIG_NOTHING), yes)
+ ifeq ($(CONFIG_EVERYTHING), yes)
+ $(error Setting CONFIG_NOTHING=yes and CONFIG_EVERYTHING=yes does not make sense)
+ endif
+ $(foreach var, $(filter CONFIG_%, $(.VARIABLES)),\
+ $(if $(filter yes, $($(var))),\
+ $(eval $(var)=no)))
+endif
+
+# Enable all features if CONFIG_EVERYTHING=yes is given
+ifeq ($(CONFIG_EVERYTHING), yes)
+$(foreach var, $(filter CONFIG_%, $(.VARIABLES)),\
+ $(if $(filter no, $($(var))),\
+ $(eval $(var)=yes)))
+endif
+
+# Disable feature groups
+ifeq ($(CONFIG_ENABLE_LIBUSB0_PROGRAMMERS), no)
+override CONFIG_PICKIT2_SPI = no
+endif
+ifeq ($(CONFIG_ENABLE_LIBUSB1_PROGRAMMERS), no)
+override CONFIG_CH341A_SPI = no
+override CONFIG_DEDIPROG = no
+endif
+ifeq ($(CONFIG_ENABLE_LIBPCI_PROGRAMMERS), no)
+override CONFIG_INTERNAL = no
+override CONFIG_NIC3COM = no
+override CONFIG_GFXNVIDIA = no
+override CONFIG_SATASII = no
+override CONFIG_ATAHPT = no
+override CONFIG_ATAVIA = no
+override CONFIG_ATAPROMISE = no
+override CONFIG_IT8212 = no
+override CONFIG_DRKAISER = no
+override CONFIG_NICREALTEK = no
+override CONFIG_NICNATSEMI = no
+override CONFIG_NICINTEL = no
+override CONFIG_NICINTEL_SPI = no
+override CONFIG_NICINTEL_EEPROM = no
+override CONFIG_OGP_SPI = no
+override CONFIG_SATAMV = no
+endif
+
+# Bitbanging SPI infrastructure, default off unless needed.
+ifeq ($(CONFIG_RAYER_SPI), yes)
+override CONFIG_BITBANG_SPI = yes
+else
+ifeq ($(CONFIG_PONY_SPI), yes)
+override CONFIG_BITBANG_SPI = yes
+else
+ifeq ($(CONFIG_INTERNAL), yes)
+override CONFIG_BITBANG_SPI = yes
+else
+ifeq ($(CONFIG_NICINTEL_SPI), yes)
+override CONFIG_BITBANG_SPI = yes
+else
+ifeq ($(CONFIG_OGP_SPI), yes)
+override CONFIG_BITBANG_SPI = yes
+else
+CONFIG_BITBANG_SPI ?= no
+endif
+endif
+endif
+endif
+endif
+
+###############################################################################
+# Handle CONFIG_* variables that depend on others set (and verified) above.
+
+# The external DMI decoder (dmidecode) does not work in libpayload. Bail out if the internal one got disabled.
+ifeq ($(TARGET_OS), libpayload)
+ifeq ($(CONFIG_INTERNAL), yes)
+ifeq ($(CONFIG_INTERNAL_DMI), no)
+UNSUPPORTED_FEATURES += CONFIG_INTERNAL_DMI=no
+else
+override CONFIG_INTERNAL_DMI = yes
+endif
+endif
+endif
+
+# Use internal DMI/SMBIOS decoder by default instead of relying on dmidecode.
+CONFIG_INTERNAL_DMI ?= yes
+
+###############################################################################
+# Programmer drivers and programmer support infrastructure.
+# Depending on the CONFIG_* variables set and verified above we set compiler flags and parameters below.
+
+FEATURE_CFLAGS += -D'CONFIG_DEFAULT_PROGRAMMER=$(CONFIG_DEFAULT_PROGRAMMER)'
+FEATURE_CFLAGS += -D'CONFIG_DEFAULT_PROGRAMMER_ARGS="$(CONFIG_DEFAULT_PROGRAMMER_ARGS)"'
+
+ifeq ($(CONFIG_INTERNAL), yes)
+FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
+PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o internal.o
+ifeq ($(ARCH), x86)
+PROGRAMMER_OBJS += it87spi.o it85spi.o sb600spi.o amd_imc.o wbsio_spi.o mcp6x_spi.o
+PROGRAMMER_OBJS += ichspi.o ich_descriptors.o dmi.o
+ifeq ($(CONFIG_INTERNAL_DMI), yes)
+FEATURE_CFLAGS += -D'CONFIG_INTERNAL_DMI=1'
+endif
+else
+endif
+NEED_LIBPCI += CONFIG_INTERNAL
+endif
+
+ifeq ($(CONFIG_SERPROG), yes)
+FEATURE_CFLAGS += -D'CONFIG_SERPROG=1'
+PROGRAMMER_OBJS += serprog.o
+NEED_SERIAL += CONFIG_SERPROG
+NEED_POSIX_SOCKETS += CONFIG_SERPROG
+endif
+
+ifeq ($(CONFIG_RAYER_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_RAYER_SPI=1'
+PROGRAMMER_OBJS += rayer_spi.o
+NEED_RAW_ACCESS += CONFIG_RAYER_SPI
+endif
+
+ifeq ($(CONFIG_PONY_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_PONY_SPI=1'
+PROGRAMMER_OBJS += pony_spi.o
+NEED_SERIAL += CONFIG_PONY_SPI
+endif
+
+ifeq ($(CONFIG_BITBANG_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_BITBANG_SPI=1'
+PROGRAMMER_OBJS += bitbang_spi.o
+endif
+
+ifeq ($(CONFIG_NIC3COM), yes)
+FEATURE_CFLAGS += -D'CONFIG_NIC3COM=1'
+PROGRAMMER_OBJS += nic3com.o
+NEED_LIBPCI += CONFIG_NIC3COM
+endif
+
+ifeq ($(CONFIG_GFXNVIDIA), yes)
+FEATURE_CFLAGS += -D'CONFIG_GFXNVIDIA=1'
+PROGRAMMER_OBJS += gfxnvidia.o
+NEED_LIBPCI += CONFIG_GFXNVIDIA
+endif
+
+ifeq ($(CONFIG_SATASII), yes)
+FEATURE_CFLAGS += -D'CONFIG_SATASII=1'
+PROGRAMMER_OBJS += satasii.o
+NEED_LIBPCI += CONFIG_SATASII
+endif
+
+ifeq ($(CONFIG_ATAHPT), yes)
+FEATURE_CFLAGS += -D'CONFIG_ATAHPT=1'
+PROGRAMMER_OBJS += atahpt.o
+NEED_LIBPCI += CONFIG_ATAHPT
+endif
+
+ifeq ($(CONFIG_ATAVIA), yes)
+FEATURE_CFLAGS += -D'CONFIG_ATAVIA=1'
+PROGRAMMER_OBJS += atavia.o
+NEED_LIBPCI += CONFIG_ATAVIA
+endif
+
+ifeq ($(CONFIG_ATAPROMISE), yes)
+FEATURE_CFLAGS += -D'CONFIG_ATAPROMISE=1'
+PROGRAMMER_OBJS += atapromise.o
+NEED_LIBPCI += CONFIG_ATAPROMISE
+endif
+
+ifeq ($(CONFIG_IT8212), yes)
+FEATURE_CFLAGS += -D'CONFIG_IT8212=1'
+PROGRAMMER_OBJS += it8212.o
+NEED_LIBPCI += CONFIG_IT8212
+endif
+
+ifeq ($(CONFIG_FT2232_SPI), yes)
+# This is a totally ugly hack.
+FEATURE_CFLAGS += $(call debug_shell,grep -q "FTDISUPPORT := yes" .features && printf "%s" "-D'CONFIG_FT2232_SPI=1'")
+NEED_LIBFTDI += CONFIG_FT2232_SPI
+PROGRAMMER_OBJS += ft2232_spi.o
+endif
+
+ifeq ($(CONFIG_USBBLASTER_SPI), yes)
+# This is a totally ugly hack.
+FEATURE_CFLAGS += $(call debug_shell,grep -q "FTDISUPPORT := yes" .features && printf "%s" "-D'CONFIG_USBBLASTER_SPI=1'")
+NEED_LIBFTDI += CONFIG_USBBLASTER_SPI
+PROGRAMMER_OBJS += usbblaster_spi.o
+endif
+
+ifeq ($(CONFIG_PICKIT2_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_PICKIT2_SPI=1'
+PROGRAMMER_OBJS += pickit2_spi.o
+NEED_LIBUSB0 += CONFIG_PICKIT2_SPI
+endif
+
+ifneq ($(NEED_LIBFTDI), )
+FTDILIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libftdi1 || $(PKG_CONFIG) --libs libftdi || printf "%s" "-lftdi -lusb")
+FEATURE_CFLAGS += $(call debug_shell,grep -q "FT232H := yes" .features && printf "%s" "-D'HAVE_FT232H=1'")
+FTDI_INCLUDES := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --cflags-only-I libftdi1)
+FEATURE_CFLAGS += $(FTDI_INCLUDES)
+FEATURE_LIBS += $(call debug_shell,grep -q "FTDISUPPORT := yes" .features && printf "%s" "$(FTDILIBS)")
+# We can't set NEED_LIBUSB0 here because that would transform libftdi auto-enabling
+# into a hard requirement for libusb, defeating the purpose of auto-enabling.
+endif
+
+ifeq ($(CONFIG_DUMMY), yes)
+FEATURE_CFLAGS += -D'CONFIG_DUMMY=1'
+PROGRAMMER_OBJS += dummyflasher.o
+endif
+
+ifeq ($(CONFIG_DRKAISER), yes)
+FEATURE_CFLAGS += -D'CONFIG_DRKAISER=1'
+PROGRAMMER_OBJS += drkaiser.o
+NEED_LIBPCI += CONFIG_DRKAISER
+endif
+
+ifeq ($(CONFIG_NICREALTEK), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICREALTEK=1'
+PROGRAMMER_OBJS += nicrealtek.o
+NEED_LIBPCI += CONFIG_NICREALTEK
+endif
+
+ifeq ($(CONFIG_NICNATSEMI), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICNATSEMI=1'
+PROGRAMMER_OBJS += nicnatsemi.o
+NEED_LIBPCI += CONFIG_NICNATSEMI
+endif
+
+ifeq ($(CONFIG_NICINTEL), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICINTEL=1'
+PROGRAMMER_OBJS += nicintel.o
+NEED_LIBPCI += CONFIG_NICINTEL
+endif
+
+ifeq ($(CONFIG_NICINTEL_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICINTEL_SPI=1'
+PROGRAMMER_OBJS += nicintel_spi.o
+NEED_LIBPCI += CONFIG_NICINTEL_SPI
+endif
+
+ifeq ($(CONFIG_NICINTEL_EEPROM), yes)
+FEATURE_CFLAGS += -D'CONFIG_NICINTEL_EEPROM=1'
+PROGRAMMER_OBJS += nicintel_eeprom.o
+NEED_LIBPCI += CONFIG_NICINTEL_EEPROM
+endif
+
+ifeq ($(CONFIG_OGP_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_OGP_SPI=1'
+PROGRAMMER_OBJS += ogp_spi.o
+NEED_LIBPCI += CONFIG_OGP_SPI
+endif
+
+ifeq ($(CONFIG_BUSPIRATE_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_BUSPIRATE_SPI=1'
+PROGRAMMER_OBJS += buspirate_spi.o
+NEED_SERIAL += CONFIG_BUSPIRATE_SPI
+endif
+
+ifeq ($(CONFIG_DEDIPROG), yes)
+FEATURE_CFLAGS += -D'CONFIG_DEDIPROG=1'
+PROGRAMMER_OBJS += dediprog.o
+NEED_LIBUSB1 += CONFIG_DEDIPROG
+endif
+
+ifeq ($(CONFIG_SATAMV), yes)
+FEATURE_CFLAGS += -D'CONFIG_SATAMV=1'
+PROGRAMMER_OBJS += satamv.o
+NEED_LIBPCI += CONFIG_SATAMV
+endif
+
+ifeq ($(CONFIG_LINUX_SPI), yes)
+# This is a totally ugly hack.
+FEATURE_CFLAGS += $(call debug_shell,grep -q "LINUX_SPI_SUPPORT := yes" .features && printf "%s" "-D'CONFIG_LINUX_SPI=1'")
+PROGRAMMER_OBJS += linux_spi.o
+endif
+
+ifeq ($(CONFIG_MSTARDDC_SPI), yes)
+# This is a totally ugly hack.
+FEATURE_CFLAGS += $(call debug_shell,grep -q "LINUX_I2C_SUPPORT := yes" .features && printf "%s" "-D'CONFIG_MSTARDDC_SPI=1'")
+NEED_LINUX_I2C += CONFIG_MSTARDDC_SPI
+PROGRAMMER_OBJS += mstarddc_spi.o
+endif
+
+ifeq ($(CONFIG_CH341A_SPI), yes)
+FEATURE_CFLAGS += -D'CONFIG_CH341A_SPI=1'
+PROGRAMMER_OBJS += ch341a_spi.o
+NEED_LIBUSB1 += CONFIG_CH341A_SPI
+endif
+
+ifneq ($(NEED_SERIAL), )
+LIB_OBJS += serial.o
+endif
+
+ifneq ($(NEED_POSIX_SOCKETS), )
+ifeq ($(TARGET_OS), SunOS)
+LIBS += -lsocket -lnsl
+endif
+endif
+
+ifneq ($(NEED_LIBPCI), )
+CHECK_LIBPCI = yes
+# This is a dirty hack, but it saves us from checking all PCI drivers and all platforms manually.
+# libpci may need raw memory, MSR or PCI port I/O on some platforms.
+# Individual drivers might have the same needs as well.
+NEED_RAW_ACCESS += $(NEED_LIBPCI)
+FEATURE_CFLAGS += -D'NEED_PCI=1'
+FEATURE_CFLAGS += $(call debug_shell,grep -q "OLD_PCI_GET_DEV := yes" .libdeps && printf "%s" "-D'OLD_PCI_GET_DEV=1'")
+
+PROGRAMMER_OBJS += pcidev.o
+ifeq ($(TARGET_OS), NetBSD)
+# The libpci we want is called libpciutils on NetBSD and needs NetBSD libpci.
+PCILIBS += -lpciutils -lpci
+else
+PCILIBS += -lpci
+endif
+endif
+
+ifneq ($(NEED_RAW_ACCESS), )
+# Raw memory, MSR or PCI port I/O access.
+FEATURE_CFLAGS += -D'NEED_RAW_ACCESS=1'
+PROGRAMMER_OBJS += physmap.o hwaccess.o
+
+ifeq ($(TARGET_OS), NetBSD)
+# For (i386|x86_64)_iopl(2).
+PCILIBS += -l$(shell uname -p)
+else
+ifeq ($(TARGET_OS), OpenBSD)
+# For (i386|amd64)_iopl(2).
+PCILIBS += -l$(shell uname -m)
+else
+ifeq ($(TARGET_OS), Darwin)
+# DirectHW framework can be found in the DirectHW library.
+PCILIBS += -framework IOKit -framework DirectHW
+endif
+endif
+endif
+
+endif
+
+ifneq ($(NEED_LIBUSB0), )
+CHECK_LIBUSB0 = yes
+FEATURE_CFLAGS += -D'NEED_LIBUSB0=1'
+USBLIBS := $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)" ; $(PKG_CONFIG) --libs libusb || printf "%s" "-lusb")
+endif
+
+ifneq ($(NEED_LIBUSB1), )
+CHECK_LIBUSB1 = yes
+FEATURE_CFLAGS += -D'NEED_LIBUSB1=1'
+# FreeBSD and DragonflyBSD use a reimplementation of libusb-1.0 that is simply called libusb
+ifeq ($(TARGET_OS),$(filter $(TARGET_OS),FreeBSD DragonFlyBSD))
+USB1LIBS += -lusb
+else
+ifeq ($(TARGET_OS),NetBSD)
+override CPPFLAGS += -I/usr/pkg/include/libusb-1.0
+USB1LIBS += -lusb-1.0
+else
+USB1LIBS += $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)"; $(PKG_CONFIG) --libs libusb-1.0 || printf "%s" "-lusb-1.0")
+override CPPFLAGS += $(call debug_shell,[ -n "$(PKG_CONFIG_LIBDIR)" ] && export PKG_CONFIG_LIBDIR="$(PKG_CONFIG_LIBDIR)"; $(PKG_CONFIG) --cflags-only-I libusb-1.0 || printf "%s" "-I/usr/include/libusb-1.0")
+endif
+endif
+endif
+
+ifeq ($(CONFIG_PRINT_WIKI), yes)
+FEATURE_CFLAGS += -D'CONFIG_PRINT_WIKI=1'
+CLI_OBJS += print_wiki.o
+endif
+
+FEATURE_CFLAGS += $(call debug_shell,grep -q "UTSNAME := yes" .features && printf "%s" "-D'HAVE_UTSNAME=1'")
+
+# We could use PULLED_IN_LIBS, but that would be ugly.
+FEATURE_LIBS += $(call debug_shell,grep -q "NEEDLIBZ := yes" .libdeps && printf "%s" "-lz")
+
+LIBFLASHROM_OBJS = $(CHIP_OBJS) $(PROGRAMMER_OBJS) $(LIB_OBJS)
+OBJS = $(CLI_OBJS) $(LIBFLASHROM_OBJS)
+
+all: hwlibs features $(PROGRAM)$(EXEC_SUFFIX) $(PROGRAM).8
+ifeq ($(ARCH), x86)
+ @+$(MAKE) -C util/ich_descriptors_tool/ TARGET_OS=$(TARGET_OS) EXEC_SUFFIX=$(EXEC_SUFFIX)
+endif
+
+$(PROGRAM)$(EXEC_SUFFIX): $(OBJS)
+ $(CC) $(LDFLAGS) -o $(PROGRAM)$(EXEC_SUFFIX) $(OBJS) $(LIBS) $(PCILIBS) $(FEATURE_LIBS) $(USBLIBS) $(USB1LIBS)
+
+libflashrom.a: $(LIBFLASHROM_OBJS)
+ $(AR) rcs $@ $^
+ $(RANLIB) $@
+
+# TAROPTIONS reduces information leakage from the packager's system.
+# If other tar programs support command line arguments for setting uid/gid of
+# stored files, they can be handled here as well.
+TAROPTIONS = $(shell LC_ALL=C tar --version|grep -q GNU && echo "--owner=root --group=root")
+
+%.o: %.c .features
+ $(CC) -MMD $(CFLAGS) $(CPPFLAGS) $(FLASHROM_CFLAGS) $(FEATURE_CFLAGS) $(SVNDEF) -o $@ -c $<
+
+# Make sure to add all names of generated binaries here.
+# This includes all frontends and libflashrom.
+# We don't use EXEC_SUFFIX here because we want to clean everything.
+clean:
+ rm -f $(PROGRAM) $(PROGRAM).exe libflashrom.a *.o *.d $(PROGRAM).8 $(PROGRAM).8.html $(BUILD_DETAILS_FILE)
+ @+$(MAKE) -C util/ich_descriptors_tool/ clean
+
+distclean: clean
+ rm -f .features .libdeps
+
+strip: $(PROGRAM)$(EXEC_SUFFIX)
+ $(STRIP) $(STRIP_ARGS) $(PROGRAM)$(EXEC_SUFFIX)
+
+# to define test programs we use verbatim variables, which get exported
+# to environment variables and are referenced with $$<varname> later
+
+define COMPILER_TEST
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+endef
+export COMPILER_TEST
+
+compiler: featuresavailable
+ @printf "Checking for a C compiler... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$COMPILER_TEST" > .test.c
+ @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test$(EXEC_SUFFIX)" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .test.c -o .test$(EXEC_SUFFIX) >&2 && \
+ echo "found." || { echo "not found."; \
+ rm -f .test.c .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @rm -f .test.c .test$(EXEC_SUFFIX)
+ @printf "Target arch is "
+ @# FreeBSD wc will output extraneous whitespace.
+ @echo $(ARCH)|wc -w|grep -q '^[[:blank:]]*1[[:blank:]]*$$' || \
+ ( echo "unknown. Aborting."; exit 1)
+ @printf "%s\n" '$(ARCH)'
+ @printf "Target OS is "
+ @# FreeBSD wc will output extraneous whitespace.
+ @echo $(TARGET_OS)|wc -w|grep -q '^[[:blank:]]*1[[:blank:]]*$$' || \
+ ( echo "unknown. Aborting."; exit 1)
+ @printf "%s\n" '$(TARGET_OS)'
+ifeq ($(TARGET_OS), libpayload)
+ @$(CC) --version 2>&1 | grep -q coreboot || \
+ ( echo "Warning: It seems you are not using coreboot's reference compiler."; \
+ echo "This might work but usually does not, please beware." )
+endif
+
+define LIBPCI_TEST
+/* Avoid a failing test due to libpci header symbol shadowing breakage */
+#define index shadow_workaround_index
+#if !defined __NetBSD__
+#include <pci/pci.h>
+#else
+#include <pciutils/pci.h>
+#endif
+struct pci_access *pacc;
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ pacc = pci_alloc();
+ return 0;
+}
+endef
+export LIBPCI_TEST
+
+define PCI_GET_DEV_TEST
+/* Avoid a failing test due to libpci header symbol shadowing breakage */
+#define index shadow_workaround_index
+#if !defined __NetBSD__
+#include <pci/pci.h>
+#else
+#include <pciutils/pci.h>
+#endif
+struct pci_access *pacc;
+struct pci_dev *dev = {0};
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ pacc = pci_alloc();
+ dev = pci_get_dev(pacc, dev->domain, dev->bus, dev->dev, 1);
+ return 0;
+}
+endef
+export PCI_GET_DEV_TEST
+
+define LIBUSB0_TEST
+#include "platform.h"
+#if IS_WINDOWS
+#include <lusb0_usb.h>
+#else
+#include <usb.h>
+#endif
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ usb_init();
+ return 0;
+}
+endef
+export LIBUSB0_TEST
+
+define LIBUSB1_TEST
+#include <stddef.h>
+#include <libusb.h>
+int main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+ libusb_init(NULL);
+ return 0;
+}
+endef
+export LIBUSB1_TEST
+
+hwlibs: compiler
+ @printf "" > .libdeps
+ifeq ($(CHECK_LIBPCI), yes)
+ @printf "Checking for libpci headers... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$LIBPCI_TEST" > .test.c
+ @printf "\nexec: %s\n" "$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >&2 && \
+ echo "found." || { echo "not found."; echo; \
+ echo "The following features require libpci: $(NEED_LIBPCI)."; \
+ echo "Please install libpci headers or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBPCI_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o; exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @printf "Checking version of pci_get_dev... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$PCI_GET_DEV_TEST" > .test.c
+ @printf "\nexec: %s\n" "$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o" >>$(BUILD_DETAILS_FILE)
+ @ { $(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >&2 && \
+ ( echo "new version (including PCI domain parameter)."; echo "OLD_PCI_GET_DEV := no" >> .libdeps ) || \
+ ( echo "old version (without PCI domain parameter)."; echo "OLD_PCI_GET_DEV := yes" >> .libdeps ) } 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE)
+ @printf "Checking if libpci is present and sufficient... " | tee -a $(BUILD_DETAILS_FILE)
+ @printf "\nexec: %s\n" "$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(PCILIBS)" >>$(BUILD_DETAILS_FILE)
+ @{ { { { $(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(PCILIBS) 2>>$(BUILD_DETAILS_FILE) >&2 && \
+ echo "yes." || { echo "no."; \
+ printf "Checking if libz+libpci are present and sufficient..." ; \
+ { printf "\nexec: %s\n" "$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(PCILIBS) -lz" >>$(BUILD_DETAILS_FILE) ; \
+ $(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(PCILIBS) -lz >&2 && \
+ echo "yes." && echo "NEEDLIBZ := yes" > .libdeps } || { echo "no."; echo; \
+ echo "The following features require libpci: $(NEED_LIBPCI)."; \
+ echo "Please install libpci (package pciutils) and/or libz or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBPCI_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @rm -f .test.c .test.o .test$(EXEC_SUFFIX)
+endif
+ifeq ($(CHECK_LIBUSB0), yes)
+ @printf "Checking for libusb-0.1/libusb-compat headers... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$LIBUSB0_TEST" > .test.c
+ @printf "\nexec: %s\n" "$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >&2 && \
+ echo "found." || { echo "not found."; echo; \
+ echo "The following features require libusb-0.1/libusb-compat: $(NEED_LIBUSB0)."; \
+ echo "Please install libusb-0.1 headers or libusb-compat headers or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBUSB0_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o; exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @printf "Checking if libusb-0.1 is usable... " | tee -a $(BUILD_DETAILS_FILE)
+ @printf "\nexec: %s\n" "$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(USBLIBS)" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(USBLIBS) >&2 && \
+ echo "yes." || { echo "no."; \
+ echo "The following features require libusb-0.1/libusb-compat: $(NEED_LIBUSB0)."; \
+ echo "Please install libusb-0.1 or libusb-compat or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBUSB0_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @rm -f .test.c .test.o .test$(EXEC_SUFFIX)
+endif
+ifeq ($(CHECK_LIBUSB1), yes)
+ @printf "Checking for libusb-1.0 headers... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$LIBUSB1_TEST" > .test.c
+ @printf "\nexec: %s\n" "$(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) -c $(CPPFLAGS) $(CFLAGS) .test.c -o .test.o >&2 && \
+ echo "found." || { echo "not found."; echo; \
+ echo "The following features require libusb-1.0: $(NEED_LIBUSB1)."; \
+ echo "Please install libusb-1.0 headers or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBUSB1_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o; exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @printf "Checking if libusb-1.0 is usable... " | tee -a $(BUILD_DETAILS_FILE)
+ @printf "\nexec: %s\n" "$(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(USB1LIBS)" >>$(BUILD_DETAILS_FILE)
+ @{ { { { { $(CC) $(LDFLAGS) .test.o -o .test$(EXEC_SUFFIX) $(LIBS) $(USB1LIBS) >&2 && \
+ echo "yes." || { echo "no."; \
+ echo "The following features require libusb-1.0: $(NEED_LIBUSB1)."; \
+ echo "Please install libusb-1.0 or disable all features"; \
+ echo "mentioned above by specifying make CONFIG_ENABLE_LIBUSB1_PROGRAMMERS=no"; \
+ echo "See README for more information."; echo; \
+ rm -f .test.c .test.o .test$(EXEC_SUFFIX); exit 1; }; } 2>>$(BUILD_DETAILS_FILE); echo $? >&3 ; } | tee -a $(BUILD_DETAILS_FILE) >&4; } 3>&1;} | { read rc ; exit ${rc}; } } 4>&1
+ @rm -f .test.c .test.o .test$(EXEC_SUFFIX)
+endif
+
+.features: features
+
+# If a user does not explicitly request a non-working feature, we should
+# silently disable it. However, if a non-working (does not compile) feature
+# is explicitly requested, we should bail out with a descriptive error message.
+# We also have to check that at least one programmer driver is enabled.
+featuresavailable:
+ifeq ($(PROGRAMMER_OBJS),)
+ @echo "You have to enable at least one programmer driver!"
+ @false
+endif
+ifneq ($(UNSUPPORTED_FEATURES), )
+ @echo "The following features are unavailable on your machine: $(UNSUPPORTED_FEATURES)"
+ @false
+endif
+
+define FTDI_TEST
+#include <stdlib.h>
+#include <ftdi.h>
+struct ftdi_context *ftdic = NULL;
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ return ftdi_init(ftdic);
+}
+endef
+export FTDI_TEST
+
+define FTDI_232H_TEST
+#include <ftdi.h>
+enum ftdi_chip_type type = TYPE_232H;
+endef
+export FTDI_232H_TEST
+
+define UTSNAME_TEST
+#include <sys/utsname.h>
+struct utsname osinfo;
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ uname (&osinfo);
+ return 0;
+}
+endef
+export UTSNAME_TEST
+
+define LINUX_SPI_TEST
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+endef
+export LINUX_SPI_TEST
+
+define LINUX_I2C_TEST
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+
+int main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+endef
+export LINUX_I2C_TEST
+
+features: compiler
+ @echo "FEATURES := yes" > .features.tmp
+ifneq ($(NEED_LIBFTDI), )
+ @printf "Checking for FTDI support... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$FTDI_TEST" > .featuretest.c
+ @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(FTDI_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(FTDILIBS) $(LIBS)" >>$(BUILD_DETAILS_FILE)
+ @ { $(CC) $(CPPFLAGS) $(CFLAGS) $(FTDI_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(FTDILIBS) $(LIBS) >&2 && \
+ ( echo "found."; echo "FTDISUPPORT := yes" >> .features.tmp ; \
+ printf "Checking for FT232H support in libftdi... " ; \
+ echo "$$FTDI_232H_TEST" >> .featuretest.c ; \
+ printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(FTDI_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(FTDILIBS) $(LIBS)" >>$(BUILD_DETAILS_FILE) ; \
+ { $(CC) $(CPPFLAGS) $(CFLAGS) $(FTDI_INCLUDES) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) $(FTDILIBS) $(LIBS) >&2 && \
+ ( echo "found."; echo "FT232H := yes" >> .features.tmp ) || \
+ ( echo "not found."; echo "FT232H := no" >> .features.tmp ) } \
+ ) || \
+ ( echo "not found."; echo "FTDISUPPORT := no" >> .features.tmp ) } \
+ 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE)
+endif
+ifeq ($(CONFIG_LINUX_SPI), yes)
+ @printf "Checking if Linux SPI headers are present... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$LINUX_SPI_TEST" > .featuretest.c
+ @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX)" >>$(BUILD_DETAILS_FILE)
+ @ { $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) >&2 && \
+ ( echo "yes."; echo "LINUX_SPI_SUPPORT := yes" >> .features.tmp ) || \
+ ( echo "no."; echo "LINUX_SPI_SUPPORT := no" >> .features.tmp ) } \
+ 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE)
+endif
+ifneq ($(NEED_LINUX_I2C), )
+ @printf "Checking if Linux I2C headers are present... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$LINUX_I2C_TEST" > .featuretest.c
+ @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX)" >>$(BUILD_DETAILS_FILE)
+ @ { $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) >&2 && \
+ ( echo "yes."; echo "LINUX_I2C_SUPPORT := yes" >> .features.tmp ) || \
+ ( echo "no."; echo "LINUX_I2C_SUPPORT := no" >> .features.tmp ) } \
+ 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE)
+endif
+ @printf "Checking for utsname support... " | tee -a $(BUILD_DETAILS_FILE)
+ @echo "$$UTSNAME_TEST" > .featuretest.c
+ @printf "\nexec: %s\n" "$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX)" >>$(BUILD_DETAILS_FILE)
+ @ { $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) .featuretest.c -o .featuretest$(EXEC_SUFFIX) >&2 && \
+ ( echo "found."; echo "UTSNAME := yes" >> .features.tmp ) || \
+ ( echo "not found."; echo "UTSNAME := no" >> .features.tmp ) } 2>>$(BUILD_DETAILS_FILE) | tee -a $(BUILD_DETAILS_FILE)
+ @$(DIFF) -q .features.tmp .features >/dev/null 2>&1 && rm .features.tmp || mv .features.tmp .features
+ @rm -f .featuretest.c .featuretest$(EXEC_SUFFIX)
+
+$(PROGRAM).8.html: $(PROGRAM).8
+ @groff -mandoc -Thtml $< >$@
+
+$(PROGRAM).8: $(PROGRAM).8.tmpl
+ @# Add the man page change date and version to the man page
+ @sed -e 's#.TH FLASHROM 8 ".*".*#.TH FLASHROM 8 "$(shell ./util/getrevision.sh -d $(PROGRAM).8.tmpl 2>/dev/null)" "$(VERSION)"#' <$< >$@
+
+install: $(PROGRAM)$(EXEC_SUFFIX) $(PROGRAM).8
+ mkdir -p $(DESTDIR)$(PREFIX)/sbin
+ mkdir -p $(DESTDIR)$(MANDIR)/man8
+ $(INSTALL) -m 0755 $(PROGRAM)$(EXEC_SUFFIX) $(DESTDIR)$(PREFIX)/sbin
+ $(INSTALL) -m 0644 $(PROGRAM).8 $(DESTDIR)$(MANDIR)/man8
+
+export: $(PROGRAM).8
+ @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME)
+ @svn export -r BASE . $(EXPORTDIR)/flashrom-$(RELEASENAME)
+ @sed "s/^SVNVERSION.*/SVNVERSION := $(SVNVERSION)/" Makefile >$(EXPORTDIR)/flashrom-$(RELEASENAME)/Makefile
+ @cp $(PROGRAM).8 "$(EXPORTDIR)/flashrom-$(RELEASENAME)/$(PROGRAM).8"
+ @svn log >$(EXPORTDIR)/flashrom-$(RELEASENAME)/ChangeLog
+ @echo Exported $(EXPORTDIR)/flashrom-$(RELEASENAME)/
+
+tarball: export
+ @tar cjf $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.bz2 -C $(EXPORTDIR)/ $(TAROPTIONS) flashrom-$(RELEASENAME)/
+ @rm -rf $(EXPORTDIR)/flashrom-$(RELEASENAME)
+ @echo Created $(EXPORTDIR)/flashrom-$(RELEASENAME).tar.bz2
+
+djgpp-dos: clean
+ make CC=i586-pc-msdosdjgpp-gcc STRIP=i586-pc-msdosdjgpp-strip
+libpayload: clean
+ make CC="CC=i386-elf-gcc lpgcc" AR=i386-elf-ar RANLIB=i386-elf-ranlib
+
+.PHONY: all install clean distclean compiler hwlibs features export tarball djgpp-dos featuresavailable libpayload
+
+# Disable implicit suffixes and built-in rules (for performance and profit)
+.SUFFIXES:
+
+-include $(OBJS:.o=.d)
diff --git a/README b/README
new file mode 100644
index 0000000..ab761f7
--- /dev/null
+++ b/README
@@ -0,0 +1,173 @@
+-------------------------------------------------------------------------------
+flashrom README
+-------------------------------------------------------------------------------
+
+flashrom is a utility for detecting, reading, writing, verifying and erasing
+flash chips. It is often used to flash BIOS/EFI/coreboot/firmware images
+in-system using a supported mainboard, but it also supports flashing of network
+cards (NICs), SATA controller cards, and other external devices which can
+program flash chips.
+
+It supports a wide range of flash chips (most commonly found in SOIC8, DIP8,
+SOIC16, WSON8, PLCC32, DIP32, TSOP32, and TSOP40 packages), which use various
+protocols such as LPC, FWH, parallel flash, or SPI.
+
+Do not use flashrom on laptops (yet)! The embedded controller (EC) present in
+many laptops might interact badly with any attempts to communicate with the
+flash chip and may brick your laptop.
+
+Please make a backup of your flash chip before writing to it.
+
+Please see the flashrom(8) manpage.
+
+
+Packaging
+---------
+
+To package flashrom and remove dependencies on subversion, either use
+make export
+or
+make tarball
+
+make export will export all flashrom files from the subversion repository at
+revision BASE into a directory named $EXPORTDIR/flashrom-$VERSION-r$SVNREVISION
+and will additionally modify the Makefile in that directory to contain the svn
+revision of the exported tree.
+
+make tarball will simply tar up the result of make export and gzip compress it.
+
+The snapshot tarballs are the result of make tarball and require no further
+processing.
+
+
+Build Instructions
+------------------
+
+To build flashrom you need to install the following software:
+
+ * pciutils+libpci (if you want support for mainboard or PCI device flashing)
+ * libusb (if you want FT2232, Dediprog or USB-Blaster support)
+ * libftdi (if you want FT2232 or USB-Blaster support)
+
+Linux et al:
+
+ * pciutils / libpci
+ * pciutils-devel / pciutils-dev / libpci-dev
+ * zlib-devel / zlib1g-dev (needed if libpci was compiled with libz support)
+
+On FreeBSD, you need the following ports:
+
+ * devel/gmake
+ * devel/libpci
+
+On OpenBSD, you need the following ports:
+
+ * devel/gmake
+ * sysutils/pciutils
+
+To compile on Linux, use:
+
+ make
+
+To compile on FreeBSD, OpenBSD or DragonFly BSD, use:
+
+ gmake
+
+To compile on Nexenta, use:
+
+ make
+
+To compile on Solaris, use:
+
+ gmake LDFLAGS="-L$pathtolibpci" CC="gcc -I$pathtopciheaders" CFLAGS=-O2
+
+To compile on NetBSD (with pciutils, libftdi, libusb installed in /usr/pkg/), use:
+
+ gmake
+
+To compile and run on Darwin/Mac OS X:
+
+ Install DirectHW from coresystems GmbH.
+ DirectHW is available at http://www.coreboot.org/DirectHW .
+
+To cross-compile on Linux for DOS:
+
+ Get packages of the DJGPP cross compiler and install them:
+ djgpp-filesystem djgpp-gcc djgpp-cpp djgpp-runtime djgpp-binutils
+ As an alternative, the DJGPP web site offers packages for download as well:
+ djcross-binutils-2.19.1-10ap.i386.rpm
+ djcross-gcc-4.3.2-8ap.i686.rpm
+ djcrx-2.04pre_20090725-13ap.i386.rpm
+ The cross toolchain packages for your distribution may have slightly different
+ names (look for packages named *djgpp*).
+
+ You will need the following library source trees containing their compiled
+ static libraries either in the parent directory of the flashrom source or
+ specify the base folder on compile time with the LIBS_BASE parameter.
+ The default as described above is equal to calling
+ 'make djgpp-dos LIBS_BASE=..'
+
+ To get and build said libraries...
+ Download pciutils 3.1.5 and apply http://flashrom.org/File:Pciutils.patch.gz
+ Compile pciutils, see README.DJGPP for instructions.
+ Download and compile http://flashrom.org/File:Libgetopt.tar.gz
+ Enter the flashrom directory.
+ Run either (change settings where appropriate)
+ make CC=i586-pc-msdosdjgpp-gcc STRIP=i586-pc-msdosdjgpp-strip
+ or (above settings hardcoded)
+ make djgpp-dos
+ To run flashrom.exe, download http://flashrom.org/File:Csdpmi7b.zip and
+ unpack CWSDPMI.EXE into the current directory or one in PATH.
+
+To cross-compile on Linux for Windows:
+
+ Get packages of the MinGW cross compiler and install them:
+ mingw32-filesystem mingw32-cross-cpp mingw32-cross-binutils mingw32-cross-gcc
+ mingw32-runtime mingw32-headers
+ The cross toolchain packages for your distribution may have slightly different
+ names (look for packages named *mingw*).
+ PCI-based programmers (internal etc.) are not supported on Windows.
+ Run (change CC= and STRIP= settings where appropriate)
+ make CC=i686-w64-mingw32-gcc STRIP=i686-w64-mingw32-strip
+
+Processor architecture dependent features:
+
+ On non-x86 architectures a few programmers don't work (yet) because they
+ use port-based I/O which is not directly available on non-x86. Those
+ programmers will be disabled automatically if you run "make".
+
+Compiler quirks:
+
+If you are using clang and if you want to enable only one driver, you may hit an
+overzealous compiler warning from clang. Compile with "make WARNERROR=no" to
+force it to continue and enjoy.
+
+Installation
+------------
+
+In order to install flashrom and the manpage into /usr/local, type:
+
+ make install
+
+For installation in a different directory use DESTDIR, e.g. like this:
+
+ make DESTDIR=/usr install
+
+If you have insufficient permissions for the destination directory, use sudo
+by adding sudo in front of the commands above.
+
+
+Contact
+-------
+
+The official flashrom website is:
+
+ http://www.flashrom.org/
+
+The IRC channel is
+
+ #flashrom at irc.freenode.net
+
+The mailing list address is
+
+ flashrom@flashrom.org
diff --git a/amd_imc.c b/amd_imc.c
new file mode 100644
index 0000000..b05390c
--- /dev/null
+++ b/amd_imc.c
@@ -0,0 +1,159 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2013 Rudolf Marek <r.marek@assembler.cz>
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+
+/* same as serverengines */
+static void enter_conf_mode_ec(uint16_t port)
+{
+ OUTB(0x5a, port);
+}
+
+static void exit_conf_mode_ec(uint16_t port)
+{
+ OUTB(0xa5, port);
+}
+
+static uint16_t get_sio_port(struct pci_dev *dev)
+{
+ uint16_t ec_port;
+
+ if (!dev) {
+ return 0;
+ }
+
+ ec_port = pci_read_word(dev, 0xa4);
+
+ /* EcPortActive? */
+ if (!(ec_port & 0x1))
+ return 0;
+
+ ec_port &= ~0x1;
+
+ return ec_port;
+}
+
+/* Wait for up to 10 ms for a response. */
+static int mbox_wait_ack(uint16_t mbox_port)
+{
+ int i = 10;
+ while (sio_read(mbox_port, 0x82) != 0xfa) {
+ if (--i == 0) {
+ msg_pwarn("IMC MBOX: Timeout!\n");
+ return 1;
+ }
+ programmer_delay(1000);
+ }
+ return 0;
+}
+
+static uint16_t mbox_get_port(uint16_t sio_port)
+{
+ uint16_t mbox_port;
+
+ enter_conf_mode_ec(sio_port);
+
+ /* Go to LDN 9, mailbox */
+ sio_write(sio_port, 7, 9);
+
+ /* MBOX inactive? */
+ if ((sio_read(sio_port, 0x30) & 1) == 0) {
+ exit_conf_mode_ec(sio_port);
+ return 0;
+ }
+
+ mbox_port = sio_read(sio_port, 0x60) << 8;
+ mbox_port |= sio_read(sio_port, 0x61);
+
+ exit_conf_mode_ec(sio_port);
+ return mbox_port;
+}
+
+/* Returns negative values when IMC is inactive, positive values on errors */
+static int imc_send_cmd(struct pci_dev *dev, uint8_t cmd)
+{
+ uint16_t sio_port;
+ uint16_t mbox_port;
+
+ /* IntegratedEcPresent? */
+ if (!(pci_read_byte(dev, 0x40) & (1 << 7)))
+ return -1;
+
+ sio_port = get_sio_port(dev);
+ if (!sio_port)
+ return -1;
+
+ msg_pdbg2("IMC SIO is at 0x%x.\n", sio_port);
+ mbox_port = mbox_get_port(sio_port);
+ if (!mbox_port)
+ return -1;
+ msg_pdbg2("IMC MBOX is at 0x%x.\n", mbox_port);
+
+ sio_write(mbox_port, 0x82, 0x0);
+ sio_write(mbox_port, 0x83, cmd);
+ sio_write(mbox_port, 0x84, 0x0);
+ /* trigger transfer 0x96 with subcommand cmd */
+ sio_write(mbox_port, 0x80, 0x96);
+
+ return mbox_wait_ack(mbox_port);
+}
+
+static int imc_resume(void *data)
+{
+ struct pci_dev *dev = data;
+ int ret = imc_send_cmd(dev, 0xb5);
+
+ if (ret != 0)
+ msg_pinfo("Resuming IMC failed)\n");
+ else
+ msg_pdbg2("IMC resumed.\n");
+ return ret;
+}
+
+int amd_imc_shutdown(struct pci_dev *dev)
+{
+ /* Try to put IMC to sleep */
+ int ret = imc_send_cmd(dev, 0xb4);
+
+ /* No IMC activity detectable, assume we are fine */
+ if (ret < 0) {
+ msg_pdbg2("No IMC found.\n");
+ return 0;
+ }
+
+ if (ret != 0) {
+ msg_perr("Shutting down IMC failed.\n");
+ return ret;
+ }
+ msg_pdbg2("Shutting down IMC successful.\n");
+
+ if (register_shutdown(imc_resume, dev))
+ return 1;
+
+ return ret;
+}
+
+#endif
diff --git a/archtest.c b/archtest.c
new file mode 100644
index 0000000..791f1a3
--- /dev/null
+++ b/archtest.c
@@ -0,0 +1,2 @@
+#include "platform.h"
+__FLASHROM_ARCH__
diff --git a/at45db.c b/at45db.c
new file mode 100644
index 0000000..b00a751
--- /dev/null
+++ b/at45db.c
@@ -0,0 +1,559 @@
+/*
+ * Support for Atmel AT45DB series DataFlash chips.
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2012 Aidan Thornton
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* Status register bits */
+#define AT45DB_READY (1<<7)
+#define AT45DB_CMP (1<<6)
+#define AT45DB_PROT (1<<1)
+#define AT45DB_POWEROF2 (1<<0)
+
+/* Opcodes */
+#define AT45DB_STATUS 0xD7 /* NB: this is a block erase command on most other chips(!). */
+#define AT45DB_DISABLE_PROTECT 0x3D, 0x2A, 0x7F, 0x9A
+#define AT45DB_READ_ARRAY 0xE8
+#define AT45DB_READ_PROTECT 0x32
+#define AT45DB_READ_LOCKDOWN 0x35
+#define AT45DB_PAGE_ERASE 0x81
+#define AT45DB_BLOCK_ERASE 0x50
+#define AT45DB_SECTOR_ERASE 0x7C
+#define AT45DB_CHIP_ERASE 0xC7
+#define AT45DB_CHIP_ERASE_ADDR 0x94809A /* Magic address. See usage. */
+#define AT45DB_BUFFER1_WRITE 0x84
+#define AT45DB_BUFFER1_PAGE_PROGRAM 0x88
+/* Buffer 2 is unused yet.
+#define AT45DB_BUFFER2_WRITE 0x87
+#define AT45DB_BUFFER2_PAGE_PROGRAM 0x89
+*/
+
+static uint8_t at45db_read_status_register(struct flashctx *flash, uint8_t *status)
+{
+ static const uint8_t cmd[] = { AT45DB_STATUS };
+
+ int ret = spi_send_command(flash, sizeof(cmd), 1, cmd, status);
+ if (ret != 0)
+ msg_cerr("Reading the status register failed!\n");
+ else
+ msg_cspew("Status register: 0x%02x.\n", *status);
+ return ret;
+}
+
+int spi_disable_blockprotect_at45db(struct flashctx *flash)
+{
+ static const uint8_t cmd[4] = { AT45DB_DISABLE_PROTECT }; /* NB: 4 bytes magic number */
+ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+ if (ret != 0) {
+ msg_cerr("Sending disable lockdown failed!\n");
+ return ret;
+ }
+ uint8_t status;
+ ret = at45db_read_status_register(flash, &status);
+ if (ret != 0 || ((status & AT45DB_PROT) != 0)) {
+ msg_cerr("Disabling lockdown failed!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static unsigned int at45db_get_sector_count(struct flashctx *flash)
+{
+ unsigned int i, j;
+ unsigned int cnt = 0;
+ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
+ if (flash->chip->block_erasers[i].block_erase == &spi_erase_at45db_sector) {
+ for (j = 0; j < NUM_ERASEREGIONS; j++) {
+ cnt += flash->chip->block_erasers[i].eraseblocks[j].count;
+ }
+ }
+ }
+ msg_cspew("%s: number of sectors=%u\n", __func__, cnt);
+ return cnt;
+}
+
+/* Reads and prettyprints protection/lockdown registers.
+ * Some elegance of the printouts had to be cut down a bit to share this code. */
+static uint8_t at45db_prettyprint_protection_register(struct flashctx *flash, uint8_t opcode, const char *regname)
+{
+ const uint8_t cmd[] = { opcode, 0, 0, 0 };
+ const size_t sec_count = at45db_get_sector_count(flash);
+ if (sec_count < 2)
+ return 0;
+
+ /* The first two sectors share the first result byte. */
+ uint8_t buf[at45db_get_sector_count(flash) - 1];
+
+ int ret = spi_send_command(flash, sizeof(cmd), sizeof(buf), cmd, buf);
+ if (ret != 0) {
+ msg_cerr("Reading the %s register failed!\n", regname);
+ return ret;
+ }
+
+ unsigned int i;
+ for (i = 0; i < sizeof(buf); i++) {
+ if (buf[i] != 0x00)
+ break;
+ if (i == sizeof(buf) - 1) {
+ msg_cdbg("No Sector is %sed.\n", regname);
+ return 0;
+ }
+ }
+
+ /* TODO: print which addresses are mapped to (un)locked sectors. */
+ msg_cdbg("Sector 0a is %s%sed.\n", ((buf[0] & 0xC0) == 0x00) ? "un" : "", regname);
+ msg_cdbg("Sector 0b is %s%sed.\n", ((buf[0] & 0x30) == 0x00) ? "un" : "", regname);
+ for (i = 1; i < sizeof(buf); i++)
+ msg_cdbg("Sector %2u is %s%sed.\n", i, (buf[i] == 0x00) ? "un" : "", regname);
+
+ return 0;
+}
+
+/* bit 7: busy flag
+ * bit 6: memory/buffer compare result
+ * bit 5-2: density (encoding see below)
+ * bit 1: protection enabled (soft or hard)
+ * bit 0: "power of 2" page size indicator (e.g. 1 means 256B; 0 means 264B)
+ *
+ * 5-2 encoding: bit 2 is always 1, bits 3-5 encode the density as "2^(bits - 1)" in Mb e.g.:
+ * AT45DB161D 1011 16Mb */
+int spi_prettyprint_status_register_at45db(struct flashctx *flash)
+{
+ uint8_t status;
+ if (at45db_read_status_register(flash, &status) != 0) {
+ return 1;
+ }
+
+ /* AT45DB321C does not support lockdown or a page size of a power of 2... */
+ const bool isAT45DB321C = (strcmp(flash->chip->name, "AT45DB321C") == 0);
+ msg_cdbg("Chip status register is 0x%02x\n", status);
+ msg_cdbg("Chip status register: Bit 7 / Ready is %sset\n", (status & AT45DB_READY) ? "" : "not ");
+ msg_cdbg("Chip status register: Bit 6 / Compare match is %sset\n", (status & AT45DB_CMP) ? "" : "not ");
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bit(status, 4);
+ spi_prettyprint_status_register_bit(status, 3);
+ spi_prettyprint_status_register_bit(status, 2);
+ const uint8_t dens = (status >> 3) & 0x7; /* Bit 2 is always 1, we use the other bits only */
+ msg_cdbg("Chip status register: Density is %u Mb\n", 1 << (dens - 1));
+ msg_cdbg("Chip status register: Bit 1 / Protection is %sset\n", (status & AT45DB_PROT) ? "" : "not ");
+
+ if (isAT45DB321C)
+ spi_prettyprint_status_register_bit(status, 0);
+ else
+ msg_cdbg("Chip status register: Bit 0 / \"Power of 2\" is %sset\n",
+ (status & AT45DB_POWEROF2) ? "" : "not ");
+
+ if (status & AT45DB_PROT)
+ at45db_prettyprint_protection_register(flash, AT45DB_READ_PROTECT, "protect");
+
+ if (!isAT45DB321C)
+ at45db_prettyprint_protection_register(flash, AT45DB_READ_LOCKDOWN, "lock");
+
+ return 0;
+}
+
+/* Probe function for AT45DB* chips that support multiple page sizes. */
+int probe_spi_at45db(struct flashctx *flash)
+{
+ uint8_t status;
+ struct flashchip *chip = flash->chip;
+
+ if (!probe_spi_rdid(flash))
+ return 0;
+
+ /* Some AT45DB* chips support two different page sizes each (e.g. 264 and 256 B). In order to tell which
+ * page size this chip has we need to read the status register. */
+ if (at45db_read_status_register(flash, &status) != 0)
+ return 0;
+
+ /* We assume sane power-of-2 page sizes and adjust the chip attributes in case this is not the case. */
+ if ((status & AT45DB_POWEROF2) == 0) {
+ chip->total_size = (chip->total_size / 32) * 33;
+ chip->page_size = (chip->page_size / 32) * 33;
+
+ unsigned int i, j;
+ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
+ struct block_eraser *eraser = &chip->block_erasers[i];
+ for (j = 0; j < NUM_ERASEREGIONS; j++) {
+ eraser->eraseblocks[j].size = (eraser->eraseblocks[j].size / 32) * 33;
+ }
+ }
+ }
+
+ switch (chip->page_size) {
+ case 256: chip->gran = write_gran_256bytes; break;
+ case 264: chip->gran = write_gran_264bytes; break;
+ case 512: chip->gran = write_gran_512bytes; break;
+ case 528: chip->gran = write_gran_528bytes; break;
+ case 1024: chip->gran = write_gran_1024bytes; break;
+ case 1056: chip->gran = write_gran_1056bytes; break;
+ default:
+ msg_cerr("%s: unknown page size %d.\n", __func__, chip->page_size);
+ return 0;
+ }
+
+ msg_cdbg2("%s: total size %i kB, page size %i B\n", __func__, chip->total_size * 1024, chip->page_size);
+
+ return 1;
+}
+
+/* In case of non-power-of-two page sizes we need to convert the address flashrom uses to the address the
+ * DataFlash chips use. The latter uses a segmented address space where the page address is encoded in the
+ * more significant bits and the offset within the page is encoded in the less significant bits. The exact
+ * partition depends on the page size.
+ */
+static unsigned int at45db_convert_addr(unsigned int addr, unsigned int page_size)
+{
+ unsigned int page_bits = address_to_bits(page_size - 1);
+ unsigned int at45db_addr = ((addr / page_size) << page_bits) | (addr % page_size);
+ msg_cspew("%s: addr=0x%x, page_size=%u, page_bits=%u -> at45db_addr=0x%x\n",
+ __func__, addr, page_size, page_bits, at45db_addr);
+ return at45db_addr;
+}
+
+int spi_read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+ if ((addr + len) > total_size) {
+ msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n",
+ __func__, addr, len, total_size);
+ return 1;
+ }
+
+ /* We have to split this up into chunks to fit within the programmer's read size limit, but those
+ * chunks can cross page boundaries. */
+ const unsigned int max_data_read = flash->mst->spi.max_data_read;
+ const unsigned int max_chunk = (max_data_read > 0) ? max_data_read : page_size;
+ while (len > 0) {
+ unsigned int chunk = min(max_chunk, len);
+ int ret = spi_nbyte_read(flash, at45db_convert_addr(addr, page_size), buf, chunk);
+ if (ret) {
+ msg_cerr("%s: error sending read command!\n", __func__);
+ return ret;
+ }
+ addr += chunk;
+ buf += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+/* Legacy continuous read, used where spi_read_at45db() is not available.
+ * The first 4 (dummy) bytes read need to be discarded. */
+int spi_read_at45db_e8(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+ if ((addr + len) > total_size) {
+ msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n",
+ __func__, addr, len, total_size);
+ return 1;
+ }
+
+ /* We have to split this up into chunks to fit within the programmer's read size limit, but those
+ * chunks can cross page boundaries. */
+ const unsigned int max_data_read = flash->mst->spi.max_data_read;
+ const unsigned int max_chunk = (max_data_read > 0) ? max_data_read : page_size;
+ while (len > 0) {
+ const unsigned int addr_at45 = at45db_convert_addr(addr, page_size);
+ const unsigned char cmd[] = {
+ AT45DB_READ_ARRAY,
+ (addr_at45 >> 16) & 0xff,
+ (addr_at45 >> 8) & 0xff,
+ (addr_at45 >> 0) & 0xff
+ };
+ /* We need to leave place for 4 dummy bytes and handle them explicitly. */
+ unsigned int chunk = min(max_chunk, len + 4);
+ uint8_t tmp[chunk];
+ int ret = spi_send_command(flash, sizeof(cmd), chunk, cmd, tmp);
+ if (ret) {
+ msg_cerr("%s: error sending read command!\n", __func__);
+ return ret;
+ }
+ /* Copy result without dummy bytes into buf and advance address counter respectively. */
+ memcpy(buf, tmp + 4, chunk - 4);
+ addr += chunk - 4;
+ buf += chunk - 4;
+ len -= chunk - 4;
+ }
+ return 0;
+}
+
+/* Returns 0 when ready, 1 on errors and timeouts. */
+static int at45db_wait_ready (struct flashctx *flash, unsigned int us, unsigned int retries)
+{
+ while (true) {
+ uint8_t status;
+ int ret = at45db_read_status_register(flash, &status);
+ if ((status & AT45DB_READY) == AT45DB_READY)
+ return 0;
+ if (ret != 0 || retries-- == 0)
+ return 1;
+ programmer_delay(us);
+ }
+}
+
+static int at45db_erase(struct flashctx *flash, uint8_t opcode, unsigned int at45db_addr, unsigned int stepsize, unsigned int retries)
+{
+ const uint8_t cmd[] = {
+ opcode,
+ (at45db_addr >> 16) & 0xff,
+ (at45db_addr >> 8) & 0xff,
+ (at45db_addr >> 0) & 0xff
+ };
+
+ /* Send erase command. */
+ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+ if (ret != 0) {
+ msg_cerr("%s: error sending erase command!\n", __func__);
+ return ret;
+ }
+
+ /* Wait for completion. */
+ ret = at45db_wait_ready(flash, stepsize, retries);
+ if (ret != 0)
+ msg_cerr("%s: chip did not become ready again after sending the erase command!\n", __func__);
+
+ return ret;
+}
+
+int spi_erase_at45db_page(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+
+ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) {
+ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen);
+ return 1;
+ }
+
+ if ((addr + blocklen) > total_size) {
+ msg_cerr("%s: tried to erase a block beyond flash boundary: addr=%u, blocklen=%u, size=%u\n",
+ __func__, addr, blocklen, total_size);
+ return 1;
+ }
+
+ /* Needs typically about 35 ms for completion, so let's wait 100 ms in 500 us steps. */
+ return at45db_erase(flash, AT45DB_PAGE_ERASE, at45db_convert_addr(addr, page_size), 500, 200);
+}
+
+int spi_erase_at45db_block(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+
+ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check blocks not pages
+ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen);
+ return 1;
+ }
+
+ if ((addr + blocklen) > total_size) {
+ msg_cerr("%s: tried to erase a block beyond flash boundary: addr=%u, blocklen=%u, size=%u\n",
+ __func__, addr, blocklen, total_size);
+ return 1;
+ }
+
+ /* Needs typically between 20 and 100 ms for completion, so let's wait 300 ms in 1 ms steps. */
+ return at45db_erase(flash, AT45DB_BLOCK_ERASE, at45db_convert_addr(addr, page_size), 1000, 300);
+}
+
+int spi_erase_at45db_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+
+ if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check sectors not pages
+ msg_cerr("%s: cannot erase partial pages: addr=%u, blocklen=%u\n", __func__, addr, blocklen);
+ return 1;
+ }
+
+ if ((addr + blocklen) > total_size) {
+ msg_cerr("%s: tried to erase a sector beyond flash boundary: addr=%u, blocklen=%u, size=%u\n",
+ __func__, addr, blocklen, total_size);
+ return 1;
+ }
+
+ /* Needs typically about 5 s for completion, so let's wait 20 seconds in 200 ms steps. */
+ return at45db_erase(flash, AT45DB_SECTOR_ERASE, at45db_convert_addr(addr, page_size), 200000, 100);
+}
+
+int spi_erase_at45db_chip(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ const unsigned int total_size = flash->chip->total_size * 1024;
+
+ if ((addr + blocklen) > total_size) {
+ msg_cerr("%s: tried to erase beyond flash boundary: addr=%u, blocklen=%u, size=%u\n",
+ __func__, addr, blocklen, total_size);
+ return 1;
+ }
+
+ /* Needs typically from about 5 to over 60 s for completion, so let's wait 100 s in 500 ms steps.
+ * NB: the address is not a real address but a magic number. This hack allows to share code. */
+ return at45db_erase(flash, AT45DB_CHIP_ERASE, AT45DB_CHIP_ERASE_ADDR, 500000, 200);
+}
+
+/* This one is really special and works only for AT45CS1282. It uses two different opcodes depending on the
+ * address and has an asymmetric layout. */
+int spi_erase_at45cs_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size * 1024;
+ const struct block_eraser be = flash->chip->block_erasers[0];
+ const unsigned int sec_0a_top = be.eraseblocks[0].size;
+ const unsigned int sec_0b_top = be.eraseblocks[0].size + be.eraseblocks[1].size;
+
+ if ((addr + blocklen) > total_size) {
+ msg_cerr("%s: tried to erase a sector beyond flash boundary: addr=%u, blocklen=%u, size=%u\n",
+ __func__, addr, blocklen, total_size);
+ return 1;
+ }
+
+ bool partial_range = false;
+ uint8_t opcode = 0x7C; /* Used for all but sector 0a. */
+ if (addr < sec_0a_top) {
+ opcode = 0x50;
+ /* One single sector of 8 pages at address 0. */
+ if (addr != 0 || blocklen != (8 * page_size))
+ partial_range = true;
+ } else if (addr < sec_0b_top) {
+ /* One single sector of 248 pages adjacent to the first. */
+ if (addr != sec_0a_top || blocklen != (248 * page_size))
+ partial_range = true;
+ } else {
+ /* The rest is filled by 63 aligned sectors of 256 pages. */
+ if ((addr % (256 * page_size)) != 0 || (blocklen % (256 * page_size)) != 0)
+ partial_range = true;
+ }
+ if (partial_range) {
+ msg_cerr("%s: cannot erase partial sectors: addr=%u, blocklen=%u\n", __func__, addr, blocklen);
+ return 1;
+ }
+
+ /* Needs up to 4 s for completion, so let's wait 20 seconds in 200 ms steps. */
+ return at45db_erase(flash, opcode, at45db_convert_addr(addr, page_size), 200000, 100);
+}
+
+static int at45db_fill_buffer1(struct flashctx *flash, const uint8_t *bytes, unsigned int off, unsigned int len)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ if ((off + len) > page_size) {
+ msg_cerr("Tried to write %u bytes at offset %u into a buffer of only %u B.\n",
+ len, off, page_size);
+ return 1;
+ }
+
+ /* Create a suitable buffer to store opcode, address and data chunks for buffer1. */
+ const int max_data_write = flash->mst->spi.max_data_write - 4;
+ const unsigned int max_chunk = (max_data_write > 0 && max_data_write <= page_size) ?
+ max_data_write : page_size;
+ uint8_t buf[4 + max_chunk];
+
+ buf[0] = AT45DB_BUFFER1_WRITE;
+ while (off < page_size) {
+ unsigned int cur_chunk = min(max_chunk, page_size - off);
+ buf[1] = (off >> 16) & 0xff;
+ buf[2] = (off >> 8) & 0xff;
+ buf[3] = (off >> 0) & 0xff;
+ memcpy(&buf[4], bytes + off, cur_chunk);
+ int ret = spi_send_command(flash, 4 + cur_chunk, 0, buf, NULL);
+ if (ret != 0) {
+ msg_cerr("%s: error sending buffer write!\n", __func__);
+ return ret;
+ }
+ off += cur_chunk;
+ }
+ return 0;
+}
+
+static int at45db_commit_buffer1(struct flashctx *flash, unsigned int at45db_addr)
+{
+ const uint8_t cmd[] = {
+ AT45DB_BUFFER1_PAGE_PROGRAM,
+ (at45db_addr >> 16) & 0xff,
+ (at45db_addr >> 8) & 0xff,
+ (at45db_addr >> 0) & 0xff
+ };
+
+ /* Send buffer to device. */
+ int ret = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+ if (ret != 0) {
+ msg_cerr("%s: error sending buffer to main memory command!\n", __func__);
+ return ret;
+ }
+
+ /* Wait for completion (typically a few ms). */
+ ret = at45db_wait_ready(flash, 250, 200); // 50 ms
+ if (ret != 0) {
+ msg_cerr("%s: chip did not become ready again!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int at45db_program_page(struct flashctx *flash, const uint8_t *buf, unsigned int at45db_addr)
+{
+ int ret = at45db_fill_buffer1(flash, buf, 0, flash->chip->page_size);
+ if (ret != 0) {
+ msg_cerr("%s: filling the buffer failed!\n", __func__);
+ return ret;
+ }
+
+ ret = at45db_commit_buffer1(flash, at45db_addr);
+ if (ret != 0) {
+ msg_cerr("%s: committing page failed!\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int spi_write_at45db(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ const unsigned int page_size = flash->chip->page_size;
+ const unsigned int total_size = flash->chip->total_size;
+
+ if ((start % page_size) != 0 || (len % page_size) != 0) {
+ msg_cerr("%s: cannot write partial pages: start=%u, len=%u\n", __func__, start, len);
+ return 1;
+ }
+
+ if ((start + len) > (total_size * 1024)) {
+ msg_cerr("%s: tried to write beyond flash boundary: start=%u, len=%u, size=%u\n",
+ __func__, start, len, total_size);
+ return 1;
+ }
+
+ unsigned int i;
+ for (i = 0; i < len; i += page_size) {
+ if (at45db_program_page(flash, buf + i, at45db_convert_addr(start + i, page_size)) != 0) {
+ msg_cerr("Writing page %u failed!\n", i);
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/atahpt.c b/atahpt.c
new file mode 100644
index 0000000..5966be8
--- /dev/null
+++ b/atahpt.c
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define BIOS_ROM_ADDR 0x90
+#define BIOS_ROM_DATA 0x94
+
+#define REG_FLASH_ACCESS 0x58
+
+#define PCI_VENDOR_ID_HPT 0x1103
+
+static uint32_t io_base_addr = 0;
+
+const struct dev_entry ata_hpt[] = {
+ {0x1103, 0x0004, NT, "Highpoint", "HPT366/368/370/370A/372/372N"},
+ {0x1103, 0x0005, NT, "Highpoint", "HPT372A/372N"},
+ {0x1103, 0x0006, NT, "Highpoint", "HPT302/302N"},
+
+ {0},
+};
+
+static void atahpt_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t atahpt_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_atahpt = {
+ .chip_readb = atahpt_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = atahpt_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int atahpt_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t reg32;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(ata_hpt, PCI_BASE_ADDRESS_4);
+ if (!dev)
+ return 1;
+
+ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_4);
+ if (!io_base_addr)
+ return 1;
+
+ /* Enable flash access. */
+ reg32 = pci_read_long(dev, REG_FLASH_ACCESS);
+ reg32 |= (1 << 24);
+ rpci_write_long(dev, REG_FLASH_ACCESS, reg32);
+
+ register_par_master(&par_master_atahpt, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void atahpt_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
+ OUTB(val, io_base_addr + BIOS_ROM_DATA);
+}
+
+static uint8_t atahpt_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
+ return INB(io_base_addr + BIOS_ROM_DATA);
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/atapromise.c b/atapromise.c
new file mode 100644
index 0000000..e0de290
--- /dev/null
+++ b/atapromise.c
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2015 Joseph C. Lehner <joseph.c.lehner@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define MAX_ROM_DECODE (32 * 1024)
+#define ADDR_MASK (MAX_ROM_DECODE - 1)
+
+/*
+ * In the absence of any public docs on the PDC2026x family, this programmer was created through a mix of
+ * reverse-engineering and trial and error.
+ *
+ * The only device tested is an Ultra100 controller, but the logic for programming the other 2026x controllers
+ * is the same, so it should, in theory, work for those as well.
+ *
+ * While the tested Ultra100 controller used a 128 kB MX29F001T chip, A16 and A15 showed continuity to ground,
+ * thus limiting the the programmer on this card to 32 kB. Without other controllers to test this programmer on,
+ * this is currently a hard limit. Note that ROM files for these controllers are 16 kB only.
+ *
+ * Since flashrom does not support accessing flash chips larger than the size limit of the programmer (the
+ * tested Ultra100 uses a 128 kB MX29F001T chip), the chip size is hackishly adjusted in atapromise_limit_chip.
+ */
+
+static uint32_t io_base_addr = 0;
+static uint32_t rom_base_addr = 0;
+
+static uint8_t *atapromise_bar = NULL;
+static size_t rom_size = 0;
+
+const struct dev_entry ata_promise[] = {
+ {0x105a, 0x4d38, NT, "Promise", "PDC20262 (FastTrak66/Ultra66)"},
+ {0x105a, 0x0d30, NT, "Promise", "PDC20265 (FastTrak100 Lite/Ultra100)"},
+ {0x105a, 0x4d30, OK, "Promise", "PDC20267 (FastTrak100/Ultra100)"},
+ {0},
+};
+
+static void atapromise_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static uint8_t atapromise_chip_readb(const struct flashctx *flash, const chipaddr addr);
+
+static const struct par_master par_master_atapromise = {
+ .chip_readb = atapromise_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = atapromise_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+void *atapromise_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ /* In case fallback_map ever returns something other than NULL. */
+ return NULL;
+}
+
+static void atapromise_limit_chip(struct flashchip *chip)
+{
+ unsigned int i, size;
+ unsigned int usable_erasers = 0;
+
+ size = chip->total_size * 1024;
+
+ /* Chip is small enough or already limited. */
+ if (size <= rom_size)
+ return;
+
+ /* Undefine all block_erasers that don't operate on the whole chip,
+ * and adjust the eraseblock size of those which do.
+ */
+ for (i = 0; i < NUM_ERASEFUNCTIONS; ++i) {
+ if (chip->block_erasers[i].eraseblocks[0].size != size) {
+ chip->block_erasers[i].eraseblocks[0].count = 0;
+ chip->block_erasers[i].block_erase = NULL;
+ } else {
+ chip->block_erasers[i].eraseblocks[0].size = rom_size;
+ usable_erasers++;
+ }
+ }
+
+ if (usable_erasers) {
+ chip->total_size = rom_size / 1024;
+ if (chip->page_size > rom_size)
+ chip->page_size = rom_size;
+ } else {
+ msg_pdbg("Failed to adjust size of chip \"%s\" (%d kB).\n", chip->name, chip->total_size);
+ }
+}
+
+int atapromise_init(void)
+{
+ struct pci_dev *dev = NULL;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(ata_promise, PCI_BASE_ADDRESS_4);
+ if (!dev)
+ return 1;
+
+ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_4) & 0xfffe;
+ if (!io_base_addr) {
+ return 1;
+ }
+
+ /* Not exactly sure what this does, because flashing seems to work
+ * well without it. However, PTIFLASH does it, so we do it too.
+ */
+ OUTB(1, io_base_addr + 0x10);
+
+ rom_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_5);
+ if (!rom_base_addr) {
+ msg_pdbg("Failed to read BAR5\n");
+ return 1;
+ }
+
+ rom_size = dev->rom_size > MAX_ROM_DECODE ? MAX_ROM_DECODE : dev->rom_size;
+ atapromise_bar = (uint8_t*)rphysmap("Promise", rom_base_addr, rom_size);
+ if (atapromise_bar == ERROR_PTR) {
+ return 1;
+ }
+
+ max_rom_decode.parallel = rom_size;
+ register_par_master(&par_master_atapromise, BUS_PARALLEL);
+
+ msg_pwarn("Do not use this device as a generic programmer. It will leave anything outside\n"
+ "the first %zu kB of the flash chip in an undefined state. It works fine for the\n"
+ "purpose of updating the firmware of this device (padding may neccessary).\n",
+ rom_size / 1024);
+
+ return 0;
+}
+
+static void atapromise_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ uint32_t data;
+
+ atapromise_limit_chip(flash->chip);
+ data = (rom_base_addr + (addr & ADDR_MASK)) << 8 | val;
+ OUTL(data, io_base_addr + 0x14);
+}
+
+static uint8_t atapromise_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ atapromise_limit_chip(flash->chip);
+ return pci_mmio_readb(atapromise_bar + (addr & ADDR_MASK));
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/atavia.c b/atavia.c
new file mode 100644
index 0000000..db29eea
--- /dev/null
+++ b/atavia.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2011 Jonathan Kollasch <jakllsch@kollasch.net>
+ * Copyright (C) 2012-2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_VIA 0x1106
+
+#define VIA_MAX_RETRIES 300
+
+#define BROM_ADDR 0x60
+
+#define BROM_DATA 0x64
+
+#define BROM_ACCESS 0x68
+#define BROM_TRIGGER 0x80
+#define BROM_WRITE 0x40
+#define BROM_SIZE_MASK 0x30
+#define BROM_SIZE_64K 0x00
+#define BROM_SIZE_32K 0x10
+#define BROM_SIZE_16K 0x20
+#define BROM_SIZE_0K 0x30
+#define BROM_BYTE_ENABLE_MASK 0x0f
+
+#define BROM_STATUS 0x69
+#define BROM_ERROR_STATUS 0x80
+
+/* Select the byte we want to access. This is done by clearing the bit corresponding to the byte we want to
+ * access, leaving the others set (yes, really). */
+#define ENABLE_BYTE(address) ((~(1 << ((address) & 3))) & BROM_BYTE_ENABLE_MASK)
+#define BYTE_OFFSET(address) (((addr) & 3) * 8)
+
+const struct dev_entry ata_via[] = {
+ {PCI_VENDOR_ID_VIA, 0x3249, DEP, "VIA", "VT6421A"},
+
+ {},
+};
+
+static void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr);
+static const struct par_master lpc_master_atavia = {
+ .chip_readb = atavia_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = atavia_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+static void *atavia_offset = NULL;
+static struct pci_dev *dev = NULL;
+
+static void atavia_prettyprint_access(uint8_t access)
+{
+ uint8_t bmask = access & BROM_BYTE_ENABLE_MASK;
+ uint8_t size = access & BROM_SIZE_MASK;
+
+ msg_pspew("Accessing byte(s):%s%s%s%s\n",
+ ((bmask & (1<<3)) == 0) ? " 3" : "",
+ ((bmask & (1<<2)) == 0) ? " 2" : "",
+ ((bmask & (1<<1)) == 0) ? " 1" : "",
+ ((bmask & (1<<0)) == 0) ? " 0" : "");
+ if (size == BROM_SIZE_0K) {
+ msg_pspew("No ROM device found.\n");
+ } else
+ msg_pspew("ROM device with %s kB attached.\n",
+ (size == BROM_SIZE_64K) ? ">=64" :
+ (size == BROM_SIZE_32K) ? "32" : "16");
+ msg_pspew("Access is a %s.\n", (access & BROM_WRITE) ? "write" : "read");
+ msg_pspew("Device is %s.\n", (access & BROM_TRIGGER) ? "busy" : "ready");
+}
+
+static bool atavia_ready(struct pci_dev *pcidev_dev)
+{
+ int try;
+ uint8_t access, status;
+ bool ready = false;
+
+ for (try = 0; try < VIA_MAX_RETRIES; try++) {
+ access = pci_read_byte(pcidev_dev, BROM_ACCESS);
+ status = pci_read_byte(pcidev_dev, BROM_STATUS);
+ if (((access & BROM_TRIGGER) == 0) && (status & BROM_ERROR_STATUS) == 0) {
+ ready = true;
+ break;
+ } else {
+ programmer_delay(1);
+ continue;
+ }
+ }
+
+ msg_pdbg2("\n%s: %s after %d tries (access=0x%02x, status=0x%02x)\n",
+ __func__, ready ? "succeeded" : "failed", try, access, status);
+ atavia_prettyprint_access(access);
+ return ready;
+}
+
+void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ return (atavia_offset != 0) ? atavia_offset : (void *)phys_addr;
+}
+
+int atavia_init(void)
+{
+ char *arg = extract_programmer_param("offset");
+ if (arg) {
+ if (strlen(arg) == 0) {
+ msg_perr("Missing argument for offset.\n");
+ free(arg);
+ return ERROR_FATAL;
+ }
+ char *endptr;
+ atavia_offset = (void *)strtoul(arg, &endptr, 0);
+ if (*endptr) {
+ msg_perr("Error: Invalid offset specified: \"%s\".\n", arg);
+ free(arg);
+ return ERROR_FATAL;
+ }
+ msg_pinfo("Mapping addresses to base %p.\n", atavia_offset);
+ }
+ free(arg);
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(ata_via, PCI_ROM_ADDRESS); /* Acutally no BAR setup needed at all. */
+ if (!dev)
+ return 1;
+
+ /* Test if a flash chip is attached. */
+ pci_write_long(dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK);
+ programmer_delay(90);
+ uint32_t base = pci_read_long(dev, PCI_ROM_ADDRESS);
+ msg_pdbg2("BROM base=0x%08x\n", base);
+ if ((base & PCI_ROM_ADDRESS_MASK) == 0) {
+ msg_pwarn("Controller thinks there is no ROM attached.\n");
+ }
+
+ if (!atavia_ready(dev)) {
+ msg_perr("Controller not ready.\n");
+ return 1;
+ }
+
+ register_par_master(&lpc_master_atavia, BUS_LPC);
+
+ return 0;
+}
+
+static void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, const chipaddr addr)
+{
+ msg_pspew("%s: 0x%02x to 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
+ pci_write_long(dev, BROM_ADDR, (addr & ~3));
+ pci_write_long(dev, BROM_DATA, val << BYTE_OFFSET(addr));
+ pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | BROM_WRITE | ENABLE_BYTE(addr));
+
+ if (!atavia_ready(dev)) {
+ msg_perr("not ready after write\n");
+ }
+}
+
+static uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ pci_write_long(dev, BROM_ADDR, (addr & ~3));
+ pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | ENABLE_BYTE(addr));
+
+ if (!atavia_ready(dev)) {
+ msg_perr("not ready after read\n");
+ }
+
+ uint8_t val = (pci_read_long(dev, BROM_DATA) >> BYTE_OFFSET(addr)) & 0xff;
+ msg_pspew("%s: 0x%02x from 0x%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
+ return val;
+}
diff --git a/bitbang_spi.c b/bitbang_spi.c
new file mode 100644
index 0000000..0b27a67
--- /dev/null
+++ b/bitbang_spi.c
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* Note that CS# is active low, so val=0 means the chip is active. */
+static void bitbang_spi_set_cs(const struct bitbang_spi_master * const master, int val)
+{
+ master->set_cs(val);
+}
+
+static void bitbang_spi_set_sck(const struct bitbang_spi_master * const master, int val)
+{
+ master->set_sck(val);
+}
+
+static void bitbang_spi_set_mosi(const struct bitbang_spi_master * const master, int val)
+{
+ master->set_mosi(val);
+}
+
+static int bitbang_spi_get_miso(const struct bitbang_spi_master * const master)
+{
+ return master->get_miso();
+}
+
+static void bitbang_spi_request_bus(const struct bitbang_spi_master * const master)
+{
+ if (master->request_bus)
+ master->request_bus();
+}
+
+static void bitbang_spi_release_bus(const struct bitbang_spi_master * const master)
+{
+ if (master->release_bus)
+ master->release_bus();
+}
+
+static int bitbang_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+
+static const struct spi_master spi_master_bitbang = {
+ .type = SPI_CONTROLLER_BITBANG,
+ .max_data_read = MAX_DATA_READ_UNLIMITED,
+ .max_data_write = MAX_DATA_WRITE_UNLIMITED,
+ .command = bitbang_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+#if 0 // until it is needed
+static int bitbang_spi_shutdown(const struct bitbang_spi_master *master)
+{
+ /* FIXME: Run bitbang_spi_release_bus here or per command? */
+ return 0;
+}
+#endif
+
+int register_spi_bitbang_master(const struct bitbang_spi_master *master)
+{
+ struct spi_master mst = spi_master_bitbang;
+ /* BITBANG_SPI_INVALID is 0, so if someone forgot to initialize ->type,
+ * we catch it here. Same goes for missing initialization of bitbanging
+ * functions.
+ */
+ if (!master || master->type == BITBANG_SPI_INVALID || !master->set_cs ||
+ !master->set_sck || !master->set_mosi || !master->get_miso ||
+ (master->request_bus && !master->release_bus) ||
+ (!master->request_bus && master->release_bus)) {
+ msg_perr("Incomplete SPI bitbang master setting!\n"
+ "Please report a bug at flashrom@flashrom.org\n");
+ return ERROR_FLASHROM_BUG;
+ }
+
+ mst.data = master;
+ register_spi_master(&mst);
+
+ /* Only mess with the bus if we're sure nobody else uses it. */
+ bitbang_spi_request_bus(master);
+ bitbang_spi_set_cs(master, 1);
+ bitbang_spi_set_sck(master, 0);
+ bitbang_spi_set_mosi(master, 0);
+ /* FIXME: Release SPI bus here and request it again for each command or
+ * don't release it now and only release it on programmer shutdown?
+ */
+ bitbang_spi_release_bus(master);
+ return 0;
+}
+
+static uint8_t bitbang_spi_rw_byte(const struct bitbang_spi_master *master,
+ uint8_t val)
+{
+ uint8_t ret = 0;
+ int i;
+
+ for (i = 7; i >= 0; i--) {
+ bitbang_spi_set_mosi(master, (val >> i) & 1);
+ programmer_delay(master->half_period);
+ bitbang_spi_set_sck(master, 1);
+ ret <<= 1;
+ ret |= bitbang_spi_get_miso(master);
+ programmer_delay(master->half_period);
+ bitbang_spi_set_sck(master, 0);
+ }
+ return ret;
+}
+
+static int bitbang_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+ const struct bitbang_spi_master *master = flash->mst->spi.data;
+
+ /* FIXME: Run bitbang_spi_request_bus here or in programmer init?
+ * Requesting and releasing the SPI bus is handled in here to allow the
+ * programmer to use its own SPI engine for native accesses.
+ */
+ bitbang_spi_request_bus(master);
+ bitbang_spi_set_cs(master, 0);
+ for (i = 0; i < writecnt; i++)
+ bitbang_spi_rw_byte(master, writearr[i]);
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = bitbang_spi_rw_byte(master, 0);
+
+ programmer_delay(master->half_period);
+ bitbang_spi_set_cs(master, 1);
+ programmer_delay(master->half_period);
+ /* FIXME: Run bitbang_spi_release_bus here or in programmer init? */
+ bitbang_spi_release_bus(master);
+
+ return 0;
+}
diff --git a/board_enable.c b/board_enable.c
new file mode 100644
index 0000000..2f0c1c0
--- /dev/null
+++ b/board_enable.c
@@ -0,0 +1,2731 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2005-2007 coresystems GmbH <stepan@coresystems.de>
+ * Copyright (C) 2006 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2007-2009 Luc Verhaegen <libv@skynet.be>
+ * Copyright (C) 2007 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the board specific flash enables.
+ */
+
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+/*
+ * Helper functions for many Winbond Super I/Os of the W836xx range.
+ */
+/* Enter extended functions */
+void w836xx_ext_enter(uint16_t port)
+{
+ OUTB(0x87, port);
+ OUTB(0x87, port);
+}
+
+/* Leave extended functions */
+void w836xx_ext_leave(uint16_t port)
+{
+ OUTB(0xAA, port);
+}
+
+/* Generic Super I/O helper functions */
+uint8_t sio_read(uint16_t port, uint8_t reg)
+{
+ OUTB(reg, port);
+ return INB(port + 1);
+}
+
+void sio_write(uint16_t port, uint8_t reg, uint8_t data)
+{
+ OUTB(reg, port);
+ OUTB(data, port + 1);
+}
+
+void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask)
+{
+ uint8_t tmp;
+
+ OUTB(reg, port);
+ tmp = INB(port + 1) & ~mask;
+ OUTB(tmp | (data & mask), port + 1);
+}
+
+/* Winbond W83697 documentation indicates that the index register has to be written for each access. */
+void sio_mask_alzheimer(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask)
+{
+ uint8_t tmp;
+
+ OUTB(reg, port);
+ tmp = INB(port + 1) & ~mask;
+ OUTB(reg, port);
+ OUTB(tmp | (data & mask), port + 1);
+}
+
+/* Not used yet. */
+#if 0
+static int enable_flash_decode_superio(void)
+{
+ int ret;
+ uint8_t tmp;
+
+ switch (superio.vendor) {
+ case SUPERIO_VENDOR_NONE:
+ ret = -1;
+ break;
+ case SUPERIO_VENDOR_ITE:
+ enter_conf_mode_ite(superio.port);
+ /* Enable flash mapping. Works for most old ITE style Super I/O. */
+ tmp = sio_read(superio.port, 0x24);
+ tmp |= 0xfc;
+ sio_write(superio.port, 0x24, tmp);
+ exit_conf_mode_ite(superio.port);
+ ret = 0;
+ break;
+ default:
+ msg_pdbg("Unhandled Super I/O type!\n");
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+#endif
+
+/*
+ * SMSC FDC37B787: Raise GPIO50
+ */
+static int fdc37b787_gpio50_raise(uint16_t port)
+{
+ uint8_t id, val;
+
+ OUTB(0x55, port); /* enter conf mode */
+ id = sio_read(port, 0x20);
+ if (id != 0x44) {
+ msg_perr("\nERROR: FDC37B787: Wrong ID 0x%02X.\n", id);
+ OUTB(0xAA, port); /* leave conf mode */
+ return -1;
+ }
+
+ sio_write(port, 0x07, 0x08); /* Select Aux I/O subdevice */
+
+ val = sio_read(port, 0xC8); /* GP50 */
+ if ((val & 0x1B) != 0x10) /* output, no invert, GPIO */
+ {
+ msg_perr("\nERROR: GPIO50 mode 0x%02X unexpected.\n", val);
+ OUTB(0xAA, port);
+ return -1;
+ }
+
+ sio_mask(port, 0xF9, 0x01, 0x01);
+
+ OUTB(0xAA, port); /* Leave conf mode */
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - Nokia IP530: Intel 440BX + PIIX4 + FDC37B787
+ */
+static int fdc37b787_gpio50_raise_3f0(void)
+{
+ return fdc37b787_gpio50_raise(0x3f0);
+}
+
+struct winbond_mux {
+ uint8_t reg; /* 0 if the corresponding pin is not muxed */
+ uint8_t data; /* reg/data/mask may be directly ... */
+ uint8_t mask; /* ... passed to sio_mask */
+};
+
+struct winbond_port {
+ const struct winbond_mux *mux; /* NULL or pointer to mux info for the 8 bits */
+ uint8_t ldn; /* LDN this GPIO register is located in */
+ uint8_t enable_bit; /* bit in 0x30 of that LDN to enable
+ the GPIO port */
+ uint8_t base; /* base register in that LDN for the port */
+};
+
+struct winbond_chip {
+ uint8_t device_id; /* reg 0x20 of the expected w83626x */
+ uint8_t gpio_port_count;
+ const struct winbond_port *port;
+};
+
+
+#define UNIMPLEMENTED_PORT {NULL, 0, 0, 0}
+
+enum winbond_id {
+ WINBOND_W83627HF_ID = 0x52,
+ WINBOND_W83627EHF_ID = 0x88,
+ WINBOND_W83627THF_ID = 0x82,
+ WINBOND_W83697HF_ID = 0x60,
+};
+
+static const struct winbond_mux w83627hf_port2_mux[8] = {
+ {0x2A, 0x01, 0x01}, /* or MIDI */
+ {0x2B, 0x80, 0x80}, /* or SPI */
+ {0x2B, 0x40, 0x40}, /* or SPI */
+ {0x2B, 0x20, 0x20}, /* or power LED */
+ {0x2B, 0x10, 0x10}, /* or watchdog */
+ {0x2B, 0x08, 0x08}, /* or infra red */
+ {0x2B, 0x04, 0x04}, /* or infra red */
+ {0x2B, 0x03, 0x03} /* or IRQ1 input */
+};
+
+static const struct winbond_port w83627hf[3] = {
+ UNIMPLEMENTED_PORT,
+ {w83627hf_port2_mux, 0x08, 0, 0xF0},
+ UNIMPLEMENTED_PORT,
+};
+
+static const struct winbond_mux w83627ehf_port2_mux[8] = {
+ {0x29, 0x06, 0x02}, /* or MIDI */
+ {0x29, 0x06, 0x02},
+ {0x24, 0x02, 0x00}, /* or SPI ROM interface */
+ {0x24, 0x02, 0x00},
+ {0x2A, 0x01, 0x01}, /* or keyboard/mouse interface */
+ {0x2A, 0x01, 0x01},
+ {0x2A, 0x01, 0x01},
+ {0x2A, 0x01, 0x01},
+};
+
+static const struct winbond_port w83627ehf[6] = {
+ UNIMPLEMENTED_PORT,
+ {w83627ehf_port2_mux, 0x09, 0, 0xE3},
+ UNIMPLEMENTED_PORT,
+ UNIMPLEMENTED_PORT,
+ UNIMPLEMENTED_PORT,
+ UNIMPLEMENTED_PORT,
+};
+
+static const struct winbond_mux w83627thf_port4_mux[8] = {
+ {0x2D, 0x01, 0x01}, /* or watchdog or VID level strap */
+ {0x2D, 0x02, 0x02}, /* or resume reset */
+ {0x2D, 0x04, 0x04}, /* or S3 input */
+ {0x2D, 0x08, 0x08}, /* or PSON# */
+ {0x2D, 0x10, 0x10}, /* or PWROK */
+ {0x2D, 0x20, 0x20}, /* or suspend LED */
+ {0x2D, 0x40, 0x40}, /* or panel switch input */
+ {0x2D, 0x80, 0x80}, /* or panel switch output */
+};
+
+static const struct winbond_port w83627thf[5] = {
+ UNIMPLEMENTED_PORT, /* GPIO1 */
+ UNIMPLEMENTED_PORT, /* GPIO2 */
+ UNIMPLEMENTED_PORT, /* GPIO3 */
+ {w83627thf_port4_mux, 0x09, 1, 0xF4},
+ UNIMPLEMENTED_PORT, /* GPIO5 */
+};
+
+static const struct winbond_chip winbond_chips[] = {
+ {WINBOND_W83627HF_ID, ARRAY_SIZE(w83627hf), w83627hf },
+ {WINBOND_W83627EHF_ID, ARRAY_SIZE(w83627ehf), w83627ehf},
+ {WINBOND_W83627THF_ID, ARRAY_SIZE(w83627thf), w83627thf},
+};
+
+#define WINBOND_SUPERIO_PORT1 0x2e
+#define WINBOND_SUPERIO_PORT2 0x4e
+
+/* We don't really care about the hardware monitor, but it offers better (more specific) device ID info than
+ * the simple device ID in the normal configuration registers.
+ * Note: This function expects to be called while the Super I/O is in config mode.
+ */
+static uint8_t w836xx_deviceid_hwmon(uint16_t sio_port)
+{
+ uint16_t hwmport;
+ uint16_t hwm_vendorid;
+ uint8_t hwm_deviceid;
+
+ sio_write(sio_port, 0x07, 0x0b); /* Select LDN 0xb (HWM). */
+ if ((sio_read(sio_port, 0x30) & (1 << 0)) != (1 << 0)) {
+ msg_pinfo("W836xx hardware monitor disabled or does not exist.\n");
+ return 0;
+ }
+ /* Get HWM base address (stored in LDN 0xb, index 0x60/0x61). */
+ hwmport = sio_read(sio_port, 0x60) << 8;
+ hwmport |= sio_read(sio_port, 0x61);
+ /* HWM address register = HWM base address + 5. */
+ hwmport += 5;
+ msg_pdbg2("W836xx Hardware Monitor at port %04x\n", hwmport);
+ /* FIXME: This busy check should happen before each HWM access. */
+ if (INB(hwmport) & 0x80) {
+ msg_pinfo("W836xx hardware monitor busy, ignoring it.\n");
+ return 0;
+ }
+ /* Set HBACS=1. */
+ sio_mask_alzheimer(hwmport, 0x4e, 0x80, 0x80);
+ /* Read upper byte of vendor ID. */
+ hwm_vendorid = sio_read(hwmport, 0x4f) << 8;
+ /* Set HBACS=0. */
+ sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x80);
+ /* Read lower byte of vendor ID. */
+ hwm_vendorid |= sio_read(hwmport, 0x4f);
+ if (hwm_vendorid != 0x5ca3) {
+ msg_pinfo("W836xx hardware monitor vendor ID weirdness: expected 0x5ca3, got %04x\n",
+ hwm_vendorid);
+ return 0;
+ }
+ /* Set Bank=0. */
+ sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x07);
+ /* Read "chip" ID. We call this one the device ID. */
+ hwm_deviceid = sio_read(hwmport, 0x58);
+ return hwm_deviceid;
+}
+
+void probe_superio_winbond(void)
+{
+ struct superio s = {0};
+ uint16_t winbond_ports[] = {WINBOND_SUPERIO_PORT1, WINBOND_SUPERIO_PORT2, 0};
+ uint16_t *i = winbond_ports;
+ uint8_t model;
+ uint8_t tmp;
+
+ s.vendor = SUPERIO_VENDOR_WINBOND;
+ for (; *i; i++) {
+ s.port = *i;
+ /* If we're already in Super I/O config more, the W836xx enter sequence won't hurt. */
+ w836xx_ext_enter(s.port);
+ model = sio_read(s.port, 0x20);
+ /* No response, no point leaving the config mode. */
+ if (model == 0xff)
+ continue;
+ /* Try to leave config mode. If the ID register is still readable, it's not a Winbond chip. */
+ w836xx_ext_leave(s.port);
+ if (model == sio_read(s.port, 0x20)) {
+ msg_pdbg("W836xx enter config mode worked or we were already in config mode. W836xx "
+ "leave config mode had no effect.\n");
+ if (model == 0x87) {
+ /* ITE IT8707F and IT8710F are special: They need the W837xx enter sequence,
+ * but they want the ITE exit sequence. Handle them here.
+ */
+ tmp = sio_read(s.port, 0x21);
+ switch (tmp) {
+ case 0x07:
+ case 0x10:
+ s.vendor = SUPERIO_VENDOR_ITE;
+ s.model = (0x87 << 8) | tmp ;
+ msg_pdbg("Found ITE Super I/O, ID 0x%04hx on port "
+ "0x%x\n", s.model, s.port);
+ register_superio(s);
+ /* Exit ITE config mode. */
+ exit_conf_mode_ite(s.port);
+ /* Restore vendor for next loop iteration. */
+ s.vendor = SUPERIO_VENDOR_WINBOND;
+ continue;
+ }
+ }
+ msg_pdbg("Active config mode, unknown reg 0x20 ID: %02x.\n", model);
+ continue;
+ }
+ /* The Super I/O reacts to W836xx enter and exit config mode, it's probably Winbond. */
+ w836xx_ext_enter(s.port);
+ s.model = sio_read(s.port, 0x20);
+ switch (s.model) {
+ case WINBOND_W83627HF_ID:
+ case WINBOND_W83627EHF_ID:
+ case WINBOND_W83627THF_ID:
+ msg_pdbg("Found Winbond Super I/O, id 0x%02hx\n", s.model);
+ register_superio(s);
+ break;
+ case WINBOND_W83697HF_ID:
+ /* This code is extremely paranoid. */
+ tmp = sio_read(s.port, 0x26) & 0x40;
+ if (((tmp == 0x00) && (s.port != WINBOND_SUPERIO_PORT1)) ||
+ ((tmp == 0x40) && (s.port != WINBOND_SUPERIO_PORT2))) {
+ msg_pdbg("Winbond Super I/O probe weirdness: Port mismatch for ID "
+ "0x%02x at port 0x%04x\n", s.model, s.port);
+ break;
+ }
+ tmp = w836xx_deviceid_hwmon(s.port);
+ /* FIXME: This might be too paranoid... */
+ if (!tmp) {
+ msg_pdbg("Probably not a Winbond Super I/O\n");
+ break;
+ }
+ if (tmp != s.model) {
+ msg_pinfo("W83 series hardware monitor device ID weirdness: expected 0x%02x, "
+ "got 0x%02x\n", WINBOND_W83697HF_ID, tmp);
+ break;
+ }
+ msg_pinfo("Found Winbond Super I/O, id 0x%02hx\n", s.model);
+ register_superio(s);
+ break;
+ }
+ w836xx_ext_leave(s.port);
+ }
+ return;
+}
+
+static const struct winbond_chip *winbond_superio_chipdef(void)
+{
+ int i, j;
+
+ for (i = 0; i < superio_count; i++) {
+ if (superios[i].vendor != SUPERIO_VENDOR_WINBOND)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(winbond_chips); j++)
+ if (winbond_chips[j].device_id == superios[i].model)
+ return &winbond_chips[j];
+ }
+ return NULL;
+}
+
+/*
+ * The chipid parameter goes away as soon as we have Super I/O matching in the
+ * board enable table. The call to winbond_superio_detect() goes away as
+ * soon as we have generic Super I/O detection code.
+ */
+static int winbond_gpio_set(uint16_t base, enum winbond_id chipid,
+ int pin, int raise)
+{
+ const struct winbond_chip *chip = NULL;
+ const struct winbond_port *gpio;
+ int port = pin / 10;
+ int bit = pin % 10;
+
+ chip = winbond_superio_chipdef();
+ if (!chip) {
+ msg_perr("\nERROR: No supported Winbond Super I/O found\n");
+ return -1;
+ }
+ if (chip->device_id != chipid) {
+ msg_perr("\nERROR: Found Winbond chip with ID 0x%x, "
+ "expected %x\n", chip->device_id, chipid);
+ return -1;
+ }
+ if (bit >= 8 || port == 0 || port > chip->gpio_port_count) {
+ msg_perr("\nERROR: winbond_gpio_set: Invalid GPIO number %d\n",
+ pin);
+ return -1;
+ }
+
+ gpio = &chip->port[port - 1];
+
+ if (gpio->ldn == 0) {
+ msg_perr("\nERROR: GPIO%d is not supported yet on this"
+ " winbond chip\n", port);
+ return -1;
+ }
+
+ w836xx_ext_enter(base);
+
+ /* Select logical device. */
+ sio_write(base, 0x07, gpio->ldn);
+
+ /* Activate logical device. */
+ sio_mask(base, 0x30, 1 << gpio->enable_bit, 1 << gpio->enable_bit);
+
+ /* Select GPIO function of that pin. */
+ if (gpio->mux && gpio->mux[bit].reg)
+ sio_mask(base, gpio->mux[bit].reg,
+ gpio->mux[bit].data, gpio->mux[bit].mask);
+
+ sio_mask(base, gpio->base + 0, 0, 1 << bit); /* Make pin output */
+ sio_mask(base, gpio->base + 2, 0, 1 << bit); /* Clear inversion */
+ sio_mask(base, gpio->base + 1, raise << bit, 1 << bit);
+
+ w836xx_ext_leave(base);
+
+ return 0;
+}
+
+/*
+ * Winbond W83627HF: Raise GPIO24.
+ *
+ * Suited for:
+ * - Agami Aruma
+ * - IWILL DK8-HTX
+ */
+static int w83627hf_gpio24_raise_2e(void)
+{
+ return winbond_gpio_set(0x2e, WINBOND_W83627HF_ID, 24, 1);
+}
+
+/*
+ * Winbond W83627HF: Raise GPIO25.
+ *
+ * Suited for:
+ * - MSI MS-6577
+ */
+static int w83627hf_gpio25_raise_2e(void)
+{
+ return winbond_gpio_set(0x2e, WINBOND_W83627HF_ID, 25, 1);
+}
+
+/*
+ * Winbond W83627EHF: Raise GPIO22.
+ *
+ * Suited for:
+ * - ASUS A8N-VM CSM: AMD Socket 939 + GeForce 6150 (C51) + MCP51
+ */
+static int w83627ehf_gpio22_raise_2e(void)
+{
+ return winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, 22, 1);
+}
+
+/*
+ * Winbond W83627THF: Raise GPIO 44.
+ *
+ * Suited for:
+ * - MSI K8T Neo2-F V2.0
+ */
+static int w83627thf_gpio44_raise_2e(void)
+{
+ return winbond_gpio_set(0x2e, WINBOND_W83627THF_ID, 44, 1);
+}
+
+/*
+ * Winbond W83627THF: Raise GPIO 44.
+ *
+ * Suited for:
+ * - MSI K8N Neo3
+ */
+static int w83627thf_gpio44_raise_4e(void)
+{
+ return winbond_gpio_set(0x4e, WINBOND_W83627THF_ID, 44, 1);
+}
+
+/*
+ * Enable MEMW# and set ROM size to max.
+ * Supported chips: W83L517D, W83697HF/F/HG, W83697SF/UF/UG
+ */
+static void w836xx_memw_enable(uint16_t port)
+{
+ w836xx_ext_enter(port);
+ if (!(sio_read(port, 0x24) & 0x02)) { /* Flash ROM enabled? */
+ /* Enable MEMW# and set ROM size select to max. (4M). */
+ sio_mask(port, 0x24, 0x28, 0x28);
+ }
+ w836xx_ext_leave(port);
+}
+
+/**
+ * Enable MEMW# and set ROM size to max.
+ * Supported chips:
+ * W83697HF/F/HG, W83697SF/UF/UG
+ */
+void w83697xx_memw_enable(uint16_t port)
+{
+ w836xx_ext_enter(port);
+ if (!(sio_read(port, 0x24) & 0x02)) { /* Flash ROM enabled? */
+ if((sio_read(port, 0x2A) & 0xF0) == 0xF0) {
+
+ /* CR24 Bits 7 & 2 must be set to 0 enable the flash ROM */
+ /* address segments 000E0000h ~ 000FFFFFh on W83697SF/UF/UG */
+ /* These bits are reserved on W83697HF/F/HG */
+ /* Shouldn't be needed though. */
+
+ /* CR28 Bit3 must be set to 1 to enable flash access to */
+ /* FFE80000h ~ FFEFFFFFh on W83697SF/UF/UG. */
+ /* This bit is reserved on W83697HF/F/HG which default to 0 */
+ sio_mask(port, 0x28, 0x08, 0x08);
+
+ /* Enable MEMW# and set ROM size select to max. (4M)*/
+ sio_mask(port, 0x24, 0x28, 0x38);
+
+ } else {
+ msg_pwarn("Warning: Flash interface in use by GPIO!\n");
+ }
+ } else {
+ msg_pinfo("BIOS ROM is disabled\n");
+ }
+ w836xx_ext_leave(port);
+}
+
+/*
+ * Suited for:
+ * - Biostar M7VIQ: VIA KM266 + VT8235
+ */
+static int w83697xx_memw_enable_2e(void)
+{
+ w83697xx_memw_enable(0x2E);
+
+ return 0;
+}
+
+
+/*
+ * Suited for:
+ * - DFI AD77: VIA KT400 + VT8235 + W83697HF
+ * - EPoX EP-8K5A2: VIA KT333 + VT8235
+ * - Albatron PM266A Pro: VIA P4M266A + VT8235
+ * - Shuttle AK31 (all versions): VIA KT266 + VT8233
+ * - ASUS A7V8X-MX SE and A7V400-MX: AMD K7 + VIA KM400A + VT8235
+ * - Tyan S2498 (Tomcat K7M): AMD Geode NX + VIA KM400 + VT8237
+ * - MSI KM4M-V and KM4AM-V: VIA KM400/KM400A + VT8237
+ * - MSI MS-6561 (745 Ultra): SiS 745 + W83697HF
+ * - MSI MS-6787 (P4MAM-V/P4MAM-L): VIA P4M266 + VT8235
+ * - ASRock K7S41: SiS 741 + SiS 963 + W83697HF
+ * - ASRock K7S41GX: SiS 741GX + SiS 963L + W83697HF
+ */
+static int w836xx_memw_enable_2e(void)
+{
+ w836xx_memw_enable(0x2E);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - Termtek TK-3370 (rev. 2.5b)
+ */
+static int w836xx_memw_enable_4e(void)
+{
+ w836xx_memw_enable(0x4E);
+
+ return 0;
+}
+
+/*
+ * Suited for all boards with ITE IT8705F.
+ * The SIS950 Super I/O probably requires a similar flash write enable.
+ */
+int it8705f_write_enable(uint8_t port)
+{
+ uint8_t tmp;
+ int ret = 0;
+
+ enter_conf_mode_ite(port);
+ tmp = sio_read(port, 0x24);
+ /* Check if at least one flash segment is enabled. */
+ if (tmp & 0xf0) {
+ /* The IT8705F will respond to LPC cycles and translate them. */
+ internal_buses_supported = BUS_PARALLEL;
+ /* Flash ROM I/F Writes Enable */
+ tmp |= 0x04;
+ msg_pdbg("Enabling IT8705F flash ROM interface write.\n");
+ if (tmp & 0x02) {
+ /* The data sheet contradicts itself about max size. */
+ max_rom_decode.parallel = 1024 * 1024;
+ msg_pinfo("IT8705F with very unusual settings.\n"
+ "Please send the output of \"flashrom -V -p internal\" to flashrom@flashrom.org\n"
+ "with \"IT8705: your board name: flashrom -V\" as the subject to help us finish\n"
+ "support for your Super I/O. Thanks.\n");
+ ret = 1;
+ } else if (tmp & 0x08) {
+ max_rom_decode.parallel = 512 * 1024;
+ } else {
+ max_rom_decode.parallel = 256 * 1024;
+ }
+ /* Safety checks. The data sheet is unclear here: Segments 1+3
+ * overlap, no segment seems to cover top - 1MB to top - 512kB.
+ * We assume that certain combinations make no sense.
+ */
+ if (((tmp & 0x02) && !(tmp & 0x08)) || /* 1 MB en, 512 kB dis */
+ (!(tmp & 0x10)) || /* 128 kB dis */
+ (!(tmp & 0x40))) { /* 256/512 kB dis */
+ msg_perr("Inconsistent IT8705F decode size!\n");
+ ret = 1;
+ }
+ if (sio_read(port, 0x25) != 0) {
+ msg_perr("IT8705F flash data pins disabled!\n");
+ ret = 1;
+ }
+ if (sio_read(port, 0x26) != 0) {
+ msg_perr("IT8705F flash address pins 0-7 disabled!\n");
+ ret = 1;
+ }
+ if (sio_read(port, 0x27) != 0) {
+ msg_perr("IT8705F flash address pins 8-15 disabled!\n");
+ ret = 1;
+ }
+ if ((sio_read(port, 0x29) & 0x10) != 0) {
+ msg_perr("IT8705F flash write enable pin disabled!\n");
+ ret = 1;
+ }
+ if ((sio_read(port, 0x29) & 0x08) != 0) {
+ msg_perr("IT8705F flash chip select pin disabled!\n");
+ ret = 1;
+ }
+ if ((sio_read(port, 0x29) & 0x04) != 0) {
+ msg_perr("IT8705F flash read strobe pin disabled!\n");
+ ret = 1;
+ }
+ if ((sio_read(port, 0x29) & 0x03) != 0) {
+ msg_perr("IT8705F flash address pins 16-17 disabled!\n");
+ /* Not really an error if you use flash chips smaller
+ * than 256 kByte, but such a configuration is unlikely.
+ */
+ ret = 1;
+ }
+ msg_pdbg("Maximum IT8705F parallel flash decode size is %u.\n",
+ max_rom_decode.parallel);
+ if (ret) {
+ msg_pinfo("Not enabling IT8705F flash write.\n");
+ } else {
+ sio_write(port, 0x24, tmp);
+ }
+ } else {
+ msg_pdbg("No IT8705F flash segment enabled.\n");
+ ret = 0;
+ }
+ exit_conf_mode_ite(port);
+
+ return ret;
+}
+
+/*
+ * The ITE IT8707F is a custom chip made by ITE exclusively for ASUS.
+ * It uses the Winbond command sequence to enter extended configuration
+ * mode and the ITE sequence to exit.
+ *
+ * Registers seems similar to the ones on ITE IT8710F.
+ */
+static int it8707f_write_enable(uint8_t port)
+{
+ uint8_t tmp;
+
+ w836xx_ext_enter(port);
+
+ /* Set bit 3 (GLB_REG_WE) of reg 0x23: Makes reg 0x24-0x2A rw */
+ tmp = sio_read(port, 0x23);
+ tmp |= (1 << 3);
+ sio_write(port, 0x23, tmp);
+
+ /* Set bit 2 (FLASH_WE) and bit 3 (FLASH_IF_EN) of reg 0x24 */
+ tmp = sio_read(port, 0x24);
+ tmp |= (1 << 2) | (1 << 3);
+ sio_write(port, 0x24, tmp);
+
+ /* Clear bit 3 (GLB_REG_WE) of reg 0x23: Makes reg 0x24-0x2A ro */
+ tmp = sio_read(port, 0x23);
+ tmp &= ~(1 << 3);
+ sio_write(port, 0x23, tmp);
+
+ exit_conf_mode_ite(port);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS P4SC-E: SiS 651 + 962 + ITE IT8707F
+ */
+static int it8707f_write_enable_2e(void)
+{
+ return it8707f_write_enable(0x2e);
+}
+
+#define PC87360_ID 0xE1
+#define PC87364_ID 0xE4
+
+static int pc8736x_gpio_set(uint8_t chipid, uint8_t gpio, int raise)
+{
+ static const int bankbase[] = {0, 4, 8, 10, 12};
+ int gpio_bank = gpio / 8;
+ int gpio_pin = gpio % 8;
+ uint16_t baseport;
+ uint8_t id, val;
+
+ if (gpio_bank > 4) {
+ msg_perr("PC8736x: Invalid GPIO %d\n", gpio);
+ return -1;
+ }
+
+ id = sio_read(0x2E, 0x20);
+ if (id != chipid) {
+ msg_perr("PC8736x: unexpected ID %02x (expected %02x)\n",
+ id, chipid);
+ return -1;
+ }
+
+ sio_write(0x2E, 0x07, 0x07); /* Select GPIO device. */
+ baseport = (sio_read(0x2E, 0x60) << 8) | sio_read(0x2E, 0x61);
+ if ((baseport & 0xFFF0) == 0xFFF0 || baseport == 0) {
+ msg_perr("PC87360: invalid GPIO base address %04x\n",
+ baseport);
+ return -1;
+ }
+ sio_mask (0x2E, 0x30, 0x01, 0x01); /* Enable logical device. */
+ sio_write(0x2E, 0xF0, gpio_bank * 16 + gpio_pin);
+ sio_mask (0x2E, 0xF1, 0x01, 0x01); /* Make pin output. */
+
+ val = INB(baseport + bankbase[gpio_bank]);
+ if (raise)
+ val |= 1 << gpio_pin;
+ else
+ val &= ~(1 << gpio_pin);
+ OUTB(val, baseport + bankbase[gpio_bank]);
+
+ return 0;
+}
+
+/*
+ * VIA VT823x: Set one of the GPIO pins.
+ */
+static int via_vt823x_gpio_set(uint8_t gpio, int raise)
+{
+ struct pci_dev *dev;
+ uint16_t base;
+ uint8_t val, bit, offset;
+
+ dev = pci_dev_find_vendorclass(0x1106, 0x0601);
+ switch (dev->device_id) {
+ case 0x3177: /* VT8235 */
+ case 0x3227: /* VT8237/VT8237R */
+ case 0x3337: /* VT8237A */
+ break;
+ default:
+ msg_perr("\nERROR: VT823x ISA bridge not found.\n");
+ return -1;
+ }
+
+ if ((gpio >= 12) && (gpio <= 15)) {
+ /* GPIO12-15 -> output */
+ val = pci_read_byte(dev, 0xE4);
+ val |= 0x10;
+ pci_write_byte(dev, 0xE4, val);
+ } else if (gpio == 9) {
+ /* GPIO9 -> Output */
+ val = pci_read_byte(dev, 0xE4);
+ val |= 0x20;
+ pci_write_byte(dev, 0xE4, val);
+ } else if (gpio == 5) {
+ val = pci_read_byte(dev, 0xE4);
+ val |= 0x01;
+ pci_write_byte(dev, 0xE4, val);
+ } else {
+ msg_perr("\nERROR: "
+ "VT823x GPIO%02d is not implemented.\n", gpio);
+ return -1;
+ }
+
+ /* We need the I/O Base Address for this board's flash enable. */
+ base = pci_read_word(dev, 0x88) & 0xff80;
+
+ offset = 0x4C + gpio / 8;
+ bit = 0x01 << (gpio % 8);
+
+ val = INB(base + offset);
+ if (raise)
+ val |= bit;
+ else
+ val &= ~bit;
+ OUTB(val, base + offset);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS M2V-MX: VIA K8M890 + VT8237A + IT8716F
+ */
+static int via_vt823x_gpio5_raise(void)
+{
+ /* On M2V-MX: GPO5 is connected to WP# and TBL#. */
+ return via_vt823x_gpio_set(5, 1);
+}
+
+/*
+ * Suited for:
+ * - VIA EPIA EK & N & NL
+ */
+static int via_vt823x_gpio9_raise(void)
+{
+ return via_vt823x_gpio_set(9, 1);
+}
+
+/*
+ * Suited for:
+ * - VIA EPIA M and MII (and maybe other CLE266 based EPIAs)
+ *
+ * We don't need to do this for EPIA M when using coreboot, GPIO15 is never
+ * lowered there.
+ */
+static int via_vt823x_gpio15_raise(void)
+{
+ return via_vt823x_gpio_set(15, 1);
+}
+
+/*
+ * Winbond W83697HF Super I/O + VIA VT8235 southbridge
+ *
+ * Suited for:
+ * - MSI KT4V and KT4V-L: AMD K7 + VIA KT400 + VT8235
+ * - MSI KT4 Ultra: AMD K7 + VIA KT400 + VT8235
+ */
+static int board_msi_kt4v(void)
+{
+ int ret;
+
+ ret = via_vt823x_gpio_set(12, 1);
+ w836xx_memw_enable(0x2E);
+
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - ASUS P5A
+ *
+ * This is rather nasty code, but there's no way to do this cleanly.
+ * We're basically talking to some unknown device on SMBus, my guess
+ * is that it is the Winbond W83781D that lives near the DIP BIOS.
+ */
+static int board_asus_p5a(void)
+{
+ uint8_t tmp;
+ int i;
+
+#define ASUSP5A_LOOP 5000
+
+ OUTB(0x00, 0xE807);
+ OUTB(0xEF, 0xE803);
+
+ OUTB(0xFF, 0xE800);
+
+ for (i = 0; i < ASUSP5A_LOOP; i++) {
+ OUTB(0xE1, 0xFF);
+ if (INB(0xE800) & 0x04)
+ break;
+ }
+
+ if (i == ASUSP5A_LOOP) {
+ msg_perr("Unable to contact device.\n");
+ return -1;
+ }
+
+ OUTB(0x20, 0xE801);
+ OUTB(0x20, 0xE1);
+
+ OUTB(0xFF, 0xE802);
+
+ for (i = 0; i < ASUSP5A_LOOP; i++) {
+ tmp = INB(0xE800);
+ if (tmp & 0x70)
+ break;
+ }
+
+ if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
+ msg_perr("Failed to read device.\n");
+ return -1;
+ }
+
+ tmp = INB(0xE804);
+ tmp &= ~0x02;
+
+ OUTB(0x00, 0xE807);
+ OUTB(0xEE, 0xE803);
+
+ OUTB(tmp, 0xE804);
+
+ OUTB(0xFF, 0xE800);
+ OUTB(0xE1, 0xFF);
+
+ OUTB(0x20, 0xE801);
+ OUTB(0x20, 0xE1);
+
+ OUTB(0xFF, 0xE802);
+
+ for (i = 0; i < ASUSP5A_LOOP; i++) {
+ tmp = INB(0xE800);
+ if (tmp & 0x70)
+ break;
+ }
+
+ if ((i == ASUSP5A_LOOP) || !(tmp & 0x10)) {
+ msg_perr("Failed to write to device.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Set GPIO lines in the Broadcom HT-1000 southbridge.
+ *
+ * It's not a Super I/O but it uses the same index/data port method.
+ */
+static int board_hp_dl145_g3_enable(void)
+{
+ /* GPIO 0 reg from PM regs */
+ /* Set GPIO 2 and 5 high, connected to flash WP# and TBL# pins. */
+ sio_mask(0xcd6, 0x44, 0x24, 0x24);
+
+ return 0;
+}
+
+/*
+ * Set GPIO lines in the Broadcom HT-1000 southbridge.
+ *
+ * It's not a Super I/O but it uses the same index/data port method.
+ */
+static int board_hp_dl165_g6_enable(void)
+{
+ /* Variant of DL145, with slightly different pin placement. */
+ sio_mask(0xcd6, 0x44, 0x80, 0x80); /* TBL# */
+ sio_mask(0xcd6, 0x46, 0x04, 0x04); /* WP# */
+
+ return 0;
+}
+
+static int board_ibm_x3455(void)
+{
+ /* Raise GPIO13. */
+ sio_mask(0xcd6, 0x45, 0x20, 0x20);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - Elitegroup GeForce6100SM-M: NVIDIA MCP61 + ITE IT8726F
+ */
+static int board_ecs_geforce6100sm_m(void)
+{
+ struct pci_dev *dev;
+ uint32_t tmp;
+
+ dev = pci_dev_find(0x10DE, 0x03EB); /* NVIDIA MCP61 SMBus. */
+ if (!dev) {
+ msg_perr("\nERROR: NVIDIA MCP61 SMBus not found.\n");
+ return -1;
+ }
+
+ tmp = pci_read_byte(dev, 0xE0);
+ tmp &= ~(1 << 3);
+ pci_write_byte(dev, 0xE0, tmp);
+
+ return 0;
+}
+
+/*
+ * Very similar to AMD 8111 IO Hub.
+ */
+static int nvidia_mcp_gpio_set(int gpio, int raise)
+{
+ struct pci_dev *dev;
+ uint16_t base, devclass;
+ uint8_t tmp;
+
+ if ((gpio < 0) || (gpio >= 0x40)) {
+ msg_perr("\nERROR: unsupported GPIO: %d.\n", gpio);
+ return -1;
+ }
+
+ /* Check for the ISA bridge first. */
+ dev = pci_dev_find_vendorclass(0x10DE, 0x0601);
+ switch (dev->device_id) {
+ case 0x0030: /* CK804 */
+ case 0x0050: /* MCP04 */
+ case 0x0060: /* MCP2 */
+ case 0x00E0: /* CK8 */
+ break;
+ case 0x0260: /* MCP51 */
+ case 0x0261: /* MCP51 */
+ case 0x0360: /* MCP55 */
+ case 0x0364: /* MCP55 */
+ /* find SMBus controller on *this* southbridge */
+ /* The infamous Tyan S2915-E has two south bridges; they are
+ easily told apart from each other by the class of the
+ LPC bridge, but have the same SMBus bridge IDs */
+ if (dev->func != 0) {
+ msg_perr("MCP LPC bridge at unexpected function"
+ " number %d\n", dev->func);
+ return -1;
+ }
+
+#if !defined(OLD_PCI_GET_DEV)
+ dev = pci_get_dev(pacc, dev->domain, dev->bus, dev->dev, 1);
+#else
+ /* pciutils/libpci before version 2.2 is too old to support
+ * PCI domains. Such old machines usually don't have domains
+ * besides domain 0, so this is not a problem.
+ */
+ dev = pci_get_dev(pacc, dev->bus, dev->dev, 1);
+#endif
+ if (!dev) {
+ msg_perr("MCP SMBus controller could not be found\n");
+ return -1;
+ }
+ devclass = pci_read_word(dev, PCI_CLASS_DEVICE);
+ if (devclass != 0x0C05) {
+ msg_perr("Unexpected device class %04x for SMBus"
+ " controller\n", devclass);
+ return -1;
+ }
+ break;
+ default:
+ msg_perr("\nERROR: no NVIDIA LPC/SMBus controller found.\n");
+ return -1;
+ }
+
+ base = pci_read_long(dev, 0x64) & 0x0000FF00; /* System control area */
+ base += 0xC0;
+
+ tmp = INB(base + gpio);
+ tmp &= ~0x0F; /* null lower nibble */
+ tmp |= 0x04; /* gpio -> output. */
+ if (raise)
+ tmp |= 0x01;
+ OUTB(tmp, base + gpio);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS A8M2N-LA (HP OEM "NodusM3-GL8E"): NVIDIA MCP51
+ * - ASUS A8N-LA (HP OEM "Nagami-GL8E"): NVIDIA MCP51
+ * - ASUS M2NBP-VM CSM: NVIDIA MCP51
+ */
+static int nvidia_mcp_gpio0_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x00, 1);
+}
+
+/*
+ * Suited for:
+ * - abit KN8 Ultra: NVIDIA CK804
+ * - abit KN9 Ultra: NVIDIA MCP55
+ */
+static int nvidia_mcp_gpio2_lower(void)
+{
+ return nvidia_mcp_gpio_set(0x02, 0);
+}
+
+/*
+ * Suited for:
+ * - Foxconn 6150K8MD-8EKRSH: Socket 939 + NVIDIA MCP51
+ * - MSI K8N Neo4(-F/-FI/-FX/Platinum): NVIDIA CK804
+ * - MSI K8NGM2-L: NVIDIA MCP51
+ * - MSI K9N SLI: NVIDIA MCP55
+ */
+static int nvidia_mcp_gpio2_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x02, 1);
+}
+
+/*
+ * Suited for:
+ * - EPoX EP-8NPA7I: Socket 754 + NVIDIA nForce4 4X
+ */
+static int nvidia_mcp_gpio4_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x04, 1);
+}
+
+/*
+ * Suited for:
+ * - HP xw9400 (Tyan S2915-E OEM): Dual(!) NVIDIA MCP55
+ *
+ * Notes: a) There are two MCP55 chips, so also two SMBus bridges on that
+ * board. We can't tell the SMBus logical devices apart, but we
+ * can tell the LPC bridge functions apart.
+ * We need to choose the SMBus bridge next to the LPC bridge with
+ * ID 0x364 and the "LPC bridge" class.
+ * b) #TBL is hardwired on that board to a pull-down. It can be
+ * overridden by connecting the two solder points next to F2.
+ */
+static int nvidia_mcp_gpio5_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x05, 1);
+}
+
+/*
+ * Suited for:
+ * - abit NF7-S: NVIDIA CK804
+ */
+static int nvidia_mcp_gpio8_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x08, 1);
+}
+
+/*
+ * Suited for:
+ * - GIGABYTE GA-K8NS Pro-939: Socket 939 + NVIDIA nForce3 + CK8
+ * - Probably other versions of the GA-K8NS
+ */
+static int nvidia_mcp_gpio0a_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x0a, 1);
+}
+
+/*
+ * Suited for:
+ * - MSI K8N Neo Platinum: Socket 754 + nForce3 Ultra + CK8
+ * - MSI K8N Neo2 Platinum: Socket 939 + nForce3 Ultra + CK8
+ */
+static int nvidia_mcp_gpio0c_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x0c, 1);
+}
+
+/*
+ * Suited for:
+ * - abit NF-M2 nView: Socket AM2 + NVIDIA MCP51
+ */
+static int nvidia_mcp_gpio4_lower(void)
+{
+ return nvidia_mcp_gpio_set(0x04, 0);
+}
+
+/*
+ * Suited for:
+ * - ASUS P5ND2-SLI Deluxe: LGA775 + nForce4 SLI + MCP04
+ */
+static int nvidia_mcp_gpio10_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x10, 1);
+}
+
+/*
+ * Suited for:
+ * - GIGABYTE GA-K8N-SLI: AMD socket 939 + NVIDIA CK804 + ITE IT8712F
+ */
+static int nvidia_mcp_gpio21_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x21, 0x01);
+}
+
+/*
+ * Suited for:
+ * - EPoX EP-8RDA3+: Socket A + nForce2 Ultra 400 + MCP2
+ */
+static int nvidia_mcp_gpio31_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x31, 0x01);
+}
+
+/*
+ * Suited for:
+ * - GIGABYTE GA-K8N51GMF: Socket 754 + Geforce 6100 + MCP51
+ * - GIGABYTE GA-K8N51GMF-9: Socket 939 + Geforce 6100 + MCP51
+ */
+static int nvidia_mcp_gpio3b_raise(void)
+{
+ return nvidia_mcp_gpio_set(0x3b, 1);
+}
+
+/*
+ * Suited for:
+ * - Sun Ultra 40 M2: Dual Socket F (1207) + MCP55
+ */
+static int board_sun_ultra_40_m2(void)
+{
+ int ret;
+ uint8_t reg;
+ uint16_t base;
+ struct pci_dev *dev;
+
+ ret = nvidia_mcp_gpio4_lower();
+ if (ret)
+ return ret;
+
+ dev = pci_dev_find(0x10de, 0x0364); /* NVIDIA MCP55 LPC bridge */
+ if (!dev) {
+ msg_perr("\nERROR: NVIDIA MCP55 LPC bridge not found.\n");
+ return -1;
+ }
+
+ base = pci_read_word(dev, 0xb4); /* some IO BAR? */
+ if (!base)
+ return -1;
+
+ reg = INB(base + 0x4b);
+ reg |= 0x10;
+ OUTB(reg, base + 0x4b);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - Artec Group DBE61 and DBE62
+ */
+static int board_artecgroup_dbe6x(void)
+{
+#define DBE6x_MSR_DIVIL_BALL_OPTS 0x51400015
+#define DBE6x_PRI_BOOT_LOC_SHIFT 2
+#define DBE6x_BOOT_OP_LATCHED_SHIFT 8
+#define DBE6x_SEC_BOOT_LOC_SHIFT 10
+#define DBE6x_PRI_BOOT_LOC (3 << DBE6x_PRI_BOOT_LOC_SHIFT)
+#define DBE6x_BOOT_OP_LATCHED (3 << DBE6x_BOOT_OP_LATCHED_SHIFT)
+#define DBE6x_SEC_BOOT_LOC (3 << DBE6x_SEC_BOOT_LOC_SHIFT)
+#define DBE6x_BOOT_LOC_FLASH 2
+#define DBE6x_BOOT_LOC_FWHUB 3
+
+ msr_t msr;
+ unsigned long boot_loc;
+
+ /* Geode only has a single core */
+ if (setup_cpu_msr(0))
+ return -1;
+
+ msr = rdmsr(DBE6x_MSR_DIVIL_BALL_OPTS);
+
+ if ((msr.lo & (DBE6x_BOOT_OP_LATCHED)) ==
+ (DBE6x_BOOT_LOC_FWHUB << DBE6x_BOOT_OP_LATCHED_SHIFT))
+ boot_loc = DBE6x_BOOT_LOC_FWHUB;
+ else
+ boot_loc = DBE6x_BOOT_LOC_FLASH;
+
+ msr.lo &= ~(DBE6x_PRI_BOOT_LOC | DBE6x_SEC_BOOT_LOC);
+ msr.lo |= ((boot_loc << DBE6x_PRI_BOOT_LOC_SHIFT) |
+ (boot_loc << DBE6x_SEC_BOOT_LOC_SHIFT));
+
+ wrmsr(DBE6x_MSR_DIVIL_BALL_OPTS, msr);
+
+ cleanup_cpu_msr();
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS A8AE-LE (Codename AmberineM; used in Compaq Presario 061)
+ * Datasheet(s) used:
+ * - AMD document 43009 "AMD SB700/710/750 Register Reference Guide" rev. 1.00
+ */
+static int amd_sbxxx_gpio9_raise(void)
+{
+ struct pci_dev *dev;
+ uint32_t reg;
+
+ dev = pci_dev_find(0x1002, 0x4372); /* AMD SMBus controller */
+ if (!dev) {
+ msg_perr("\nERROR: AMD SMBus Controller (0x4372) not found.\n");
+ return -1;
+ }
+
+ reg = pci_read_long(dev, 0xA8); /* GPIO_12_to_4_Cntrl CI_Reg: A8h-ABh */
+ /* enable output (0: enable, 1: tristate):
+ GPIO9 output enable is at bit 5 in 0xA9 */
+ reg &= ~((uint32_t)1<<(8+5));
+ /* raise:
+ GPIO9 output register is at bit 5 in 0xA8 */
+ reg |= (1<<5);
+ pci_write_long(dev, 0xA8, reg);
+
+ return 0;
+}
+
+/*
+ * Helper function to raise/drop a given gpo line on Intel PIIX4{,E,M}.
+ */
+static int intel_piix4_gpo_set(unsigned int gpo, int raise)
+{
+ unsigned int gpo_byte, gpo_bit;
+ struct pci_dev *dev;
+ uint32_t tmp, base;
+
+ /* GPO{0,8,27,28,30} are always available. */
+ static const uint32_t nonmuxed_gpos = 0x58000101;
+
+ static const struct {unsigned int reg, mask, value; } piix4_gpo[] = {
+ {0},
+ {0xB0, 0x0001, 0x0000}, /* GPO1... */
+ {0xB0, 0x0001, 0x0000},
+ {0xB0, 0x0001, 0x0000},
+ {0xB0, 0x0001, 0x0000},
+ {0xB0, 0x0001, 0x0000},
+ {0xB0, 0x0001, 0x0000},
+ {0xB0, 0x0001, 0x0000}, /* ...GPO7: GENCFG bit 0 */
+ {0},
+ {0xB0, 0x0100, 0x0000}, /* GPO9: GENCFG bit 8 */
+ {0xB0, 0x0200, 0x0000}, /* GPO10: GENCFG bit 9 */
+ {0xB0, 0x0400, 0x0000}, /* GPO11: GENCFG bit 10 */
+ {0x4E, 0x0100, 0x0000}, /* GPO12... */
+ {0x4E, 0x0100, 0x0000},
+ {0x4E, 0x0100, 0x0000}, /* ...GPO14: XBCS bit 8 */
+ {0xB2, 0x0002, 0x0002}, /* GPO15... */
+ {0xB2, 0x0002, 0x0002}, /* ...GPO16: GENCFG bit 17 */
+ {0xB2, 0x0004, 0x0004}, /* GPO17: GENCFG bit 18 */
+ {0xB2, 0x0008, 0x0008}, /* GPO18: GENCFG bit 19 */
+ {0xB2, 0x0010, 0x0010}, /* GPO19: GENCFG bit 20 */
+ {0xB2, 0x0020, 0x0020}, /* GPO20: GENCFG bit 21 */
+ {0xB2, 0x0040, 0x0040}, /* GPO21: GENCFG bit 22 */
+ {0xB2, 0x1000, 0x1000}, /* GPO22... */
+ {0xB2, 0x1000, 0x1000}, /* ...GPO23: GENCFG bit 28 */
+ {0xB2, 0x2000, 0x2000}, /* GPO24: GENCFG bit 29 */
+ {0xB2, 0x4000, 0x4000}, /* GPO25: GENCFG bit 30 */
+ {0xB2, 0x8000, 0x8000}, /* GPO26: GENCFG bit 31 */
+ {0},
+ {0},
+ {0x4E, 0x0100, 0x0000}, /* ...GPO29: XBCS bit 8 */
+ {0}
+ };
+
+ dev = pci_dev_find(0x8086, 0x7110); /* Intel PIIX4 ISA bridge */
+ if (!dev) {
+ msg_perr("\nERROR: Intel PIIX4 ISA bridge not found.\n");
+ return -1;
+ }
+
+ /* Sanity check. */
+ if (gpo > 30) {
+ msg_perr("\nERROR: Intel PIIX4 has no GPO%d.\n", gpo);
+ return -1;
+ }
+
+ if ((((1 << gpo) & nonmuxed_gpos) == 0) &&
+ ((pci_read_word(dev, piix4_gpo[gpo].reg) & piix4_gpo[gpo].mask) !=
+ piix4_gpo[gpo].value)) {
+ msg_perr("\nERROR: PIIX4 GPO%d not programmed for output.\n", gpo);
+ return -1;
+ }
+
+ dev = pci_dev_find(0x8086, 0x7113); /* Intel PIIX4 PM */
+ if (!dev) {
+ msg_perr("\nERROR: Intel PIIX4 PM not found.\n");
+ return -1;
+ }
+
+ /* PM IO base */
+ base = pci_read_long(dev, 0x40) & 0x0000FFC0;
+
+ gpo_byte = gpo >> 3;
+ gpo_bit = gpo & 7;
+ tmp = INB(base + 0x34 + gpo_byte); /* GPO register */
+ if (raise)
+ tmp |= 0x01 << gpo_bit;
+ else
+ tmp &= ~(0x01 << gpo_bit);
+ OUTB(tmp, base + 0x34 + gpo_byte);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS OPLX-M
+ * - ASUS P2B-N
+ */
+static int intel_piix4_gpo18_lower(void)
+{
+ return intel_piix4_gpo_set(18, 0);
+}
+
+/*
+ * Suited for:
+ * - MSI MS-6163 v2 (MS-6163 Pro): Intel 440BX + PIIX4E + Winbond W83977EF
+ */
+static int intel_piix4_gpo14_raise(void)
+{
+ return intel_piix4_gpo_set(14, 1);
+}
+
+/*
+ * Suited for:
+ * - EPoX EP-BX3
+ */
+static int intel_piix4_gpo22_raise(void)
+{
+ return intel_piix4_gpo_set(22, 1);
+}
+
+/*
+ * Suited for:
+ * - abit BM6
+ */
+static int intel_piix4_gpo26_lower(void)
+{
+ return intel_piix4_gpo_set(26, 0);
+}
+
+/*
+ * Suited for:
+ * - Intel SE440BX-2
+ */
+static int intel_piix4_gpo27_lower(void)
+{
+ return intel_piix4_gpo_set(27, 0);
+}
+
+/*
+ * Suited for:
+ * - Dell OptiPlex GX1
+ */
+static int intel_piix4_gpo30_lower(void)
+{
+ return intel_piix4_gpo_set(30, 0);
+}
+
+/*
+ * Set a GPIO line on a given Intel ICH LPC controller.
+ */
+static int intel_ich_gpio_set(int gpio, int raise)
+{
+ /* Table mapping the different Intel ICH LPC chipsets. */
+ static struct {
+ uint16_t id;
+ uint8_t base_reg;
+ uint32_t bank0;
+ uint32_t bank1;
+ uint32_t bank2;
+ } intel_ich_gpio_table[] = {
+ {0x2410, 0x58, 0x0FE30000, 0, 0}, /* 82801AA (ICH) */
+ {0x2420, 0x58, 0x0FE30000, 0, 0}, /* 82801AB (ICH0) */
+ {0x2440, 0x58, 0x1BFF391B, 0, 0}, /* 82801BA (ICH2) */
+ {0x244C, 0x58, 0x1A23399B, 0, 0}, /* 82801BAM (ICH2M) */
+ {0x2450, 0x58, 0x1BFF0000, 0, 0}, /* 82801E (C-ICH) */
+ {0x2480, 0x58, 0x1BFF0000, 0x00000FFF, 0}, /* 82801CA (ICH3-S) */
+ {0x248C, 0x58, 0x1A230000, 0x00000FFF, 0}, /* 82801CAM (ICH3-M) */
+ {0x24C0, 0x58, 0x1BFF0000, 0x00000FFF, 0}, /* 82801DB/DBL (ICH4/ICH4-L) */
+ {0x24CC, 0x58, 0x1A030000, 0x00000FFF, 0}, /* 82801DBM (ICH4-M) */
+ {0x24D0, 0x58, 0x1BFF0000, 0x00030305, 0}, /* 82801EB/ER (ICH5/ICH5R) */
+ {0x2640, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FB/FR (ICH6/ICH6R) */
+ {0x2641, 0x48, 0x1BFF0000, 0x00030307, 0}, /* 82801FBM (ICH6M) */
+ {0x27B0, 0x48, 0xFFFFFFFF, 0x000300FF, 0}, /* 82801GDH (ICH7 DH) */
+ {0x27B8, 0x48, 0xFFFFFFFF, 0x000300FF, 0}, /* 82801GB/GR (ICH7 Family) */
+ {0x27B9, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GBM (ICH7-M) */
+ {0x27BD, 0x48, 0xFFEBFFFE, 0x000300FE, 0}, /* 82801GHM (ICH7-M DH) */
+ {0x2810, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HB/HR (ICH8/R) */
+ {0x2811, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HBM (ICH8M-E) */
+ {0x2812, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HH (ICH8DH) */
+ {0x2814, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HO (ICH8DO) */
+ {0x2815, 0x48, 0xFFFFFFFF, 0x00FF0FFF, 0}, /* 82801HEM (ICH8M) */
+ {0x2912, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IH (ICH9DH) */
+ {0x2914, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IO (ICH9DO) */
+ {0x2916, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IR (ICH9R) */
+ {0x2917, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IEM (ICH9M-E) */
+ {0x2918, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IB (ICH9) */
+ {0x2919, 0x48, 0xFFFFFFFF, 0x00FFFFFF, 0}, /* 82801IBM (ICH9M) */
+ {0x3A14, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JDO (ICH10DO) */
+ {0x3A16, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JIR (ICH10R) */
+ {0x3A18, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JIB (ICH10) */
+ {0x3A1A, 0x48, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000100}, /* 82801JD (ICH10D) */
+ {0, 0, 0, 0, 0} /* end marker */
+ };
+
+ struct pci_dev *dev;
+ uint16_t base;
+ uint32_t tmp;
+ int i, allowed;
+
+ /* First, look for a known LPC bridge */
+ for (dev = pacc->devices; dev; dev = dev->next) {
+ uint16_t device_class;
+ /* libpci before version 2.2.4 does not store class info. */
+ device_class = pci_read_word(dev, PCI_CLASS_DEVICE);
+ if ((dev->vendor_id == 0x8086) &&
+ (device_class == 0x0601)) { /* ISA bridge */
+ /* Is this device in our list? */
+ for (i = 0; intel_ich_gpio_table[i].id; i++)
+ if (dev->device_id == intel_ich_gpio_table[i].id)
+ break;
+
+ if (intel_ich_gpio_table[i].id)
+ break;
+ }
+ }
+
+ if (!dev) {
+ msg_perr("\nERROR: No known Intel LPC bridge found.\n");
+ return -1;
+ }
+
+ /*
+ * According to the datasheets, all Intel ICHs have the GPIO bar 5:1
+ * strapped to zero. From some mobile ICH9 version on, this becomes
+ * 6:1. The mask below catches all.
+ */
+ base = pci_read_word(dev, intel_ich_gpio_table[i].base_reg) & 0xFFC0;
+
+ /* Check whether the line is allowed. */
+ if (gpio < 32)
+ allowed = (intel_ich_gpio_table[i].bank0 >> gpio) & 0x01;
+ else if (gpio < 64)
+ allowed = (intel_ich_gpio_table[i].bank1 >> (gpio - 32)) & 0x01;
+ else
+ allowed = (intel_ich_gpio_table[i].bank2 >> (gpio - 64)) & 0x01;
+
+ if (!allowed) {
+ msg_perr("\nERROR: This Intel LPC bridge does not allow"
+ " setting GPIO%02d\n", gpio);
+ return -1;
+ }
+
+ msg_pdbg("\nIntel ICH LPC bridge: %sing GPIO%02d.\n",
+ raise ? "Rais" : "Dropp", gpio);
+
+ if (gpio < 32) {
+ /* Set line to GPIO. */
+ tmp = INL(base);
+ /* ICH/ICH0 multiplexes 27/28 on the line set. */
+ if ((gpio == 28) &&
+ ((dev->device_id == 0x2410) || (dev->device_id == 0x2420)))
+ tmp |= 1 << 27;
+ else
+ tmp |= 1 << gpio;
+ OUTL(tmp, base);
+
+ /* As soon as we are talking to ICH8 and above, this register
+ decides whether we can set the gpio or not. */
+ if (dev->device_id > 0x2800) {
+ tmp = INL(base);
+ if (!(tmp & (1 << gpio))) {
+ msg_perr("\nERROR: This Intel LPC bridge"
+ " does not allow setting GPIO%02d\n",
+ gpio);
+ return -1;
+ }
+ }
+
+ /* Set GPIO to OUTPUT. */
+ tmp = INL(base + 0x04);
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x04);
+
+ /* Raise GPIO line. */
+ tmp = INL(base + 0x0C);
+ if (raise)
+ tmp |= 1 << gpio;
+ else
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x0C);
+ } else if (gpio < 64) {
+ gpio -= 32;
+
+ /* Set line to GPIO. */
+ tmp = INL(base + 0x30);
+ tmp |= 1 << gpio;
+ OUTL(tmp, base + 0x30);
+
+ /* As soon as we are talking to ICH8 and above, this register
+ decides whether we can set the gpio or not. */
+ if (dev->device_id > 0x2800) {
+ tmp = INL(base + 30);
+ if (!(tmp & (1 << gpio))) {
+ msg_perr("\nERROR: This Intel LPC bridge"
+ " does not allow setting GPIO%02d\n",
+ gpio + 32);
+ return -1;
+ }
+ }
+
+ /* Set GPIO to OUTPUT. */
+ tmp = INL(base + 0x34);
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x34);
+
+ /* Raise GPIO line. */
+ tmp = INL(base + 0x38);
+ if (raise)
+ tmp |= 1 << gpio;
+ else
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x38);
+ } else {
+ gpio -= 64;
+
+ /* Set line to GPIO. */
+ tmp = INL(base + 0x40);
+ tmp |= 1 << gpio;
+ OUTL(tmp, base + 0x40);
+
+ tmp = INL(base + 40);
+ if (!(tmp & (1 << gpio))) {
+ msg_perr("\nERROR: This Intel LPC bridge does "
+ "not allow setting GPIO%02d\n", gpio + 64);
+ return -1;
+ }
+
+ /* Set GPIO to OUTPUT. */
+ tmp = INL(base + 0x44);
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x44);
+
+ /* Raise GPIO line. */
+ tmp = INL(base + 0x48);
+ if (raise)
+ tmp |= 1 << gpio;
+ else
+ tmp &= ~(1 << gpio);
+ OUTL(tmp, base + 0x48);
+ }
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - abit IP35: Intel P35 + ICH9R
+ * - abit IP35 Pro: Intel P35 + ICH9R
+ * - ASUS P5LD2
+ * - ASUS P5LD2-MQ
+ * - ASUS P5LD2-VM
+ * - ASUS P5LD2-VM DH
+ */
+static int intel_ich_gpio16_raise(void)
+{
+ return intel_ich_gpio_set(16, 1);
+}
+
+/*
+ * Suited for:
+ * - HP Puffer2-UL8E (ASUS PTGD-LA OEM): LGA775 + 915 + ICH6
+ */
+static int intel_ich_gpio18_raise(void)
+{
+ return intel_ich_gpio_set(18, 1);
+}
+
+/*
+ * Suited for:
+ * - MSI MS-7046: LGA775 + 915P + ICH6
+ */
+static int intel_ich_gpio19_raise(void)
+{
+ return intel_ich_gpio_set(19, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS P5BV-R: LGA775 + 3200 + ICH7
+ */
+static int intel_ich_gpio20_raise(void)
+{
+ return intel_ich_gpio_set(20, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS CUSL2-C: Intel socket370 + 815 + ICH2
+ * - ASUS P4B266LM (Sony Vaio PCV-RX650): socket478 + 845D + ICH2
+ * - ASUS P4C800-E Deluxe: socket478 + 875P + ICH5
+ * - ASUS P4P800: Intel socket478 + 865PE + ICH5R
+ * - ASUS P4P800-E Deluxe: Intel socket478 + 865PE + ICH5R
+ * - ASUS P4P800-VM: Intel socket478 + 865PE + ICH5R
+ * - ASUS P4P800-X: Intel socket478 + 865PE + ICH5R
+ * - ASUS P5GD1 Pro: Intel LGA 775 + 915P + ICH6R
+ * - ASUS P5GD2 Premium: Intel LGA775 + 915G + ICH6R
+ * - ASUS P5GDC Deluxe: Intel socket775 + 915P + ICH6R
+ * - ASUS P5PE-VM: Intel LGA775 + 865G + ICH5
+ * - ASUS TUSL2-C: Intel socket370 + 815 + ICH2
+ * - Samsung Polaris 32: socket478 + 865P + ICH5
+ */
+static int intel_ich_gpio21_raise(void)
+{
+ return intel_ich_gpio_set(21, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS P4B266: socket478 + Intel 845D + ICH2
+ * - ASUS P4B533-E: socket478 + 845E + ICH4
+ * - ASUS P4B-MX variant in HP Vectra VL420 SFF: socket478 + 845D + ICH2
+ * - TriGem Anaheim-3: socket370 + Intel 810 + ICH
+ */
+static int intel_ich_gpio22_raise(void)
+{
+ return intel_ich_gpio_set(22, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS A8Jm (laptop): Intel 945 + ICH7
+ * - ASUS P5LP-LE used in ...
+ * - HP Media Center m7270.fr Desktop PC as "Lithium-UL8E"
+ * - Epson Endeavor MT7700
+ */
+static int intel_ich_gpio34_raise(void)
+{
+ return intel_ich_gpio_set(34, 1);
+}
+
+/*
+ * Suited for:
+ * - AOpen i945GMx-VFX: Intel 945GM + ICH7-M used in ...
+ * - FSC ESPRIMO Q5010 (SMBIOS: D2544-B1)
+ */
+static int intel_ich_gpio38_raise(void)
+{
+ return intel_ich_gpio_set(38, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS M6Ne (laptop): socket 479M (guessed) + Intel 855PM + ICH4-M
+ */
+static int intel_ich_gpio43_raise(void)
+{
+ return intel_ich_gpio_set(43, 1);
+}
+
+/*
+ * Suited for:
+ * - HP Vectra VL400: 815 + ICH + PC87360
+ */
+static int board_hp_vl400(void)
+{
+ int ret;
+ ret = intel_ich_gpio_set(25, 1); /* Master write enable ? */
+ if (!ret)
+ ret = pc8736x_gpio_set(PC87360_ID, 0x09, 1); /* #WP ? */
+ if (!ret)
+ ret = pc8736x_gpio_set(PC87360_ID, 0x27, 1); /* #TBL */
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - HP e-Vectra P2706T: 810E + ICH + PC87364
+ */
+static int board_hp_p2706t(void)
+{
+ int ret;
+ ret = pc8736x_gpio_set(PC87364_ID, 0x25, 1);
+ if (!ret)
+ ret = pc8736x_gpio_set(PC87364_ID, 0x26, 1);
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - Dell PowerEdge 1850: Intel PPGA604 + E7520 + ICH5R
+ * - ASRock P4i65GV: Intel Socket478 + 865GV + ICH5R
+ * - ASRock 775i65G: Intel LGA 775 + 865G + ICH5
+ * - MSI MS-6391 (845 Pro4): Intel Socket478 + 845 + ICH2
+ */
+static int intel_ich_gpio23_raise(void)
+{
+ return intel_ich_gpio_set(23, 1);
+}
+
+/*
+ * Suited for:
+ * - GIGABYTE GA-6IEM: Intel Socket370 + i815 + ICH2
+ * - GIGABYTE GA-8IRML: Intel Socket478 + i845 + ICH2
+ */
+static int intel_ich_gpio25_raise(void)
+{
+ return intel_ich_gpio_set(25, 1);
+}
+
+/*
+ * Suited for:
+ * - IBASE MB899: i945GM + ICH7
+ */
+static int intel_ich_gpio26_raise(void)
+{
+ return intel_ich_gpio_set(26, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS DSAN-DX
+ * - P4SD-LA (HP OEM): i865 + ICH5
+ * - GIGABYTE GA-8IP775: 865P + ICH5
+ * - GIGABYTE GA-8PE667 Ultra 2: socket 478 + i845PE + ICH4
+ * - MSI MS-6788-40 (aka 848P Neo-V)
+ */
+static int intel_ich_gpio32_raise(void)
+{
+ return intel_ich_gpio_set(32, 1);
+}
+
+/*
+ * Suited for:
+ * - AOpen i975Xa-YDG: i975X + ICH7 + W83627EHF
+ */
+static int board_aopen_i975xa_ydg(void)
+{
+ int ret;
+
+ /* Vendor BIOS ends up in LDN6... maybe the board enable is wrong,
+ * or perhaps it's not needed at all?
+ * The regs it tries to touch are 0xF0, 0xF1, 0xF2 which means if it
+ * were in the right LDN, it would have to be GPIO1 or GPIO3.
+ */
+/*
+ ret = winbond_gpio_set(0x2e, WINBOND_W83627EHF_ID, x, 0)
+ if (!ret)
+*/
+ ret = intel_ich_gpio_set(33, 1);
+
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - Acorp 6A815EPD: socket 370 + intel 815 + ICH2
+ */
+static int board_acorp_6a815epd(void)
+{
+ int ret;
+
+ /* Lower Blocks Lock -- pin 7 of PLCC32 */
+ ret = intel_ich_gpio_set(22, 1);
+ if (!ret) /* Top Block Lock -- pin 8 of PLCC32 */
+ ret = intel_ich_gpio_set(23, 1);
+
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - Kontron 986LCD-M: Socket478 + 915GM + ICH7R
+ */
+static int board_kontron_986lcd_m(void)
+{
+ int ret;
+
+ ret = intel_ich_gpio_set(34, 1); /* #TBL */
+ if (!ret)
+ ret = intel_ich_gpio_set(35, 1); /* #WP */
+
+ return ret;
+}
+
+/*
+ * Suited for:
+ * - Soyo SY-7VCA: Pro133A + VT82C686
+ */
+static int via_apollo_gpo_set(int gpio, int raise)
+{
+ struct pci_dev *dev;
+ uint32_t base, tmp;
+
+ /* VT82C686 power management */
+ dev = pci_dev_find(0x1106, 0x3057);
+ if (!dev) {
+ msg_perr("\nERROR: VT82C686 PM device not found.\n");
+ return -1;
+ }
+
+ msg_pdbg("\nVIA Apollo ACPI: %sing GPIO%02d.\n",
+ raise ? "Rais" : "Dropp", gpio);
+
+ /* Select GPO function on multiplexed pins. */
+ tmp = pci_read_byte(dev, 0x54);
+ switch (gpio) {
+ case 0:
+ tmp &= ~0x03;
+ break;
+ case 1:
+ tmp |= 0x04;
+ break;
+ case 2:
+ tmp |= 0x08;
+ break;
+ case 3:
+ tmp |= 0x10;
+ break;
+ }
+ pci_write_byte(dev, 0x54, tmp);
+
+ /* PM IO base */
+ base = pci_read_long(dev, 0x48) & 0x0000FF00;
+
+ /* Drop GPO0 */
+ tmp = INL(base + 0x4C);
+ if (raise)
+ tmp |= 1U << gpio;
+ else
+ tmp &= ~(1U << gpio);
+ OUTL(tmp, base + 0x4C);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - abit VT6X4: Pro133x + VT82C686A
+ * - abit VA6: Pro133x + VT82C686A
+ */
+static int via_apollo_gpo4_lower(void)
+{
+ return via_apollo_gpo_set(4, 0);
+}
+
+/*
+ * Suited for:
+ * - Soyo SY-7VCA: Pro133A + VT82C686
+ */
+static int via_apollo_gpo0_lower(void)
+{
+ return via_apollo_gpo_set(0, 0);
+}
+
+/*
+ * Enable some GPIO pin on SiS southbridge and enables SIO flash writes.
+ *
+ * Suited for:
+ * - MSI 651M-L: SiS651 / SiS962
+ * - GIGABYTE GA-8SIMLFS 2.0
+ * - GIGABYTE GA-8SIMLH
+ */
+static int sis_gpio0_raise_and_w836xx_memw(void)
+{
+ struct pci_dev *dev;
+ uint16_t base, temp;
+
+ dev = pci_dev_find(0x1039, 0x0962);
+ if (!dev) {
+ msg_perr("Expected south bridge not found\n");
+ return 1;
+ }
+
+ base = pci_read_word(dev, 0x74);
+ temp = INW(base + 0x68);
+ temp &= ~(1 << 0); /* Make pin output? */
+ OUTW(temp, base + 0x68);
+
+ temp = INW(base + 0x64);
+ temp |= (1 << 0); /* Raise output? */
+ OUTW(temp, base + 0x64);
+
+ w836xx_memw_enable(0x2E);
+
+ return 0;
+}
+
+/*
+ * Find the runtime registers of an SMSC Super I/O, after verifying its
+ * chip ID.
+ *
+ * Returns the base port of the runtime register block, or 0 on error.
+ */
+static uint16_t smsc_find_runtime(uint16_t sio_port, uint16_t chip_id,
+ uint8_t logical_device)
+{
+ uint16_t rt_port = 0;
+
+ /* Verify the chip ID. */
+ OUTB(0x55, sio_port); /* Enable configuration. */
+ if (sio_read(sio_port, 0x20) != chip_id) {
+ msg_perr("\nERROR: SMSC Super I/O not found.\n");
+ goto out;
+ }
+
+ /* If the runtime block is active, get its address. */
+ sio_write(sio_port, 0x07, logical_device);
+ if (sio_read(sio_port, 0x30) & 1) {
+ rt_port = (sio_read(sio_port, 0x60) << 8)
+ | sio_read(sio_port, 0x61);
+ }
+
+ if (rt_port == 0) {
+ msg_perr("\nERROR: "
+ "Super I/O runtime interface not available.\n");
+ }
+out:
+ OUTB(0xaa, sio_port); /* Disable configuration. */
+ return rt_port;
+}
+
+/*
+ * Disable write protection on the Mitac 6513WU. WP# on the FWH is
+ * connected to GP30 on the Super I/O, and TBL# is always high.
+ */
+static int board_mitac_6513wu(void)
+{
+ struct pci_dev *dev;
+ uint16_t rt_port;
+ uint8_t val;
+
+ dev = pci_dev_find(0x8086, 0x2410); /* Intel 82801AA ISA bridge */
+ if (!dev) {
+ msg_perr("\nERROR: Intel 82801AA ISA bridge not found.\n");
+ return -1;
+ }
+
+ rt_port = smsc_find_runtime(0x4e, 0x54 /* LPC47U33x */, 0xa);
+ if (rt_port == 0)
+ return -1;
+
+ /* Configure the GPIO pin. */
+ val = INB(rt_port + 0x33); /* GP30 config */
+ val &= ~0x87; /* Output, non-inverted, GPIO, push/pull */
+ OUTB(val, rt_port + 0x33);
+
+ /* Disable write protection. */
+ val = INB(rt_port + 0x4d); /* GP3 values */
+ val |= 0x01; /* Set GP30 high. */
+ OUTB(val, rt_port + 0x4d);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - abit AV8: Socket939 + K8T800Pro + VT8237
+ */
+static int board_abit_av8(void)
+{
+ uint8_t val;
+
+ /* Raise GPO pins GP22 & GP23 */
+ val = INB(0x404E);
+ val |= 0xC0;
+ OUTB(val, 0x404E);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS A7V333: VIA KT333 + VT8233A + IT8703F
+ * - ASUS A7V8X: VIA KT400 + VT8235 + IT8703F
+ */
+static int it8703f_gpio51_raise(void)
+{
+ uint16_t id, base;
+ uint8_t tmp;
+
+ /* Find the IT8703F. */
+ w836xx_ext_enter(0x2E);
+ id = (sio_read(0x2E, 0x20) << 8) | sio_read(0x2E, 0x21);
+ w836xx_ext_leave(0x2E);
+
+ if (id != 0x8701) {
+ msg_perr("\nERROR: IT8703F Super I/O not found.\n");
+ return -1;
+ }
+
+ /* Get the GP567 I/O base. */
+ w836xx_ext_enter(0x2E);
+ sio_write(0x2E, 0x07, 0x0C);
+ base = (sio_read(0x2E, 0x60) << 8) | sio_read(0x2E, 0x61);
+ w836xx_ext_leave(0x2E);
+
+ if (!base) {
+ msg_perr("\nERROR: Failed to read IT8703F Super I/O GPIO"
+ " Base.\n");
+ return -1;
+ }
+
+ /* Raise GP51. */
+ tmp = INB(base);
+ tmp |= 0x02;
+ OUTB(tmp, base);
+
+ return 0;
+}
+
+/*
+ * General routine for raising/dropping GPIO lines on the ITE IT87xx.
+ */
+static int it87_gpio_set(unsigned int gpio, int raise)
+{
+ int allowed, sio;
+ unsigned int port;
+ uint16_t base, sioport;
+ uint8_t tmp;
+
+ /* IT87 GPIO configuration table */
+ static const struct it87cfg {
+ uint16_t id;
+ uint8_t base_reg;
+ uint32_t bank0;
+ uint32_t bank1;
+ uint32_t bank2;
+ } it87_gpio_table[] = {
+ {0x8712, 0x62, 0xCFF3FC00, 0x00FCFF3F, 0},
+ {0x8718, 0x62, 0xCFF37C00, 0xF3FCDF3F, 0x0000000F},
+ {0, 0, 0, 0, 0} /* end marker */
+ };
+ const struct it87cfg *cfg = NULL;
+
+ /* Find the Super I/O in the probed list */
+ for (sio = 0; sio < superio_count; sio++) {
+ int i;
+ if (superios[sio].vendor != SUPERIO_VENDOR_ITE)
+ continue;
+
+ /* Is this device in our list? */
+ for (i = 0; it87_gpio_table[i].id; i++)
+ if (superios[sio].model == it87_gpio_table[i].id) {
+ cfg = &it87_gpio_table[i];
+ goto found;
+ }
+ }
+
+ if (cfg == NULL) {
+ msg_perr("\nERROR: No IT87 Super I/O GPIO configuration "
+ "found.\n");
+ return -1;
+ }
+
+found:
+ /* Check whether the gpio is allowed. */
+ if (gpio < 32)
+ allowed = (cfg->bank0 >> gpio) & 0x01;
+ else if (gpio < 64)
+ allowed = (cfg->bank1 >> (gpio - 32)) & 0x01;
+ else if (gpio < 96)
+ allowed = (cfg->bank2 >> (gpio - 64)) & 0x01;
+ else
+ allowed = 0;
+
+ if (!allowed) {
+ msg_perr("\nERROR: IT%02X does not allow setting GPIO%02u.\n",
+ cfg->id, gpio);
+ return -1;
+ }
+
+ /* Read the Simple I/O Base Address Register */
+ sioport = superios[sio].port;
+ enter_conf_mode_ite(sioport);
+ sio_write(sioport, 0x07, 0x07);
+ base = (sio_read(sioport, cfg->base_reg) << 8) |
+ sio_read(sioport, cfg->base_reg + 1);
+ exit_conf_mode_ite(sioport);
+
+ if (!base) {
+ msg_perr("\nERROR: Failed to read IT87 Super I/O GPIO Base.\n");
+ return -1;
+ }
+
+ msg_pdbg("Using IT87 GPIO base 0x%04x\n", base);
+
+ port = gpio / 10 - 1;
+ gpio %= 10;
+
+ /* set GPIO. */
+ tmp = INB(base + port);
+ if (raise)
+ tmp |= 1 << gpio;
+ else
+ tmp &= ~(1 << gpio);
+ OUTB(tmp, base + port);
+
+ return 0;
+}
+
+/*
+ * Suited for:
+ * - ASUS A7N8X-VM/400: NVIDIA nForce2 IGP2 + IT8712F
+ */
+static int it8712f_gpio12_raise(void)
+{
+ return it87_gpio_set(12, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS A7V600-X: VIA KT600 + VT8237 + IT8712F
+ * - ASUS A7V8X-X: VIA KT400 + VT8235 + IT8712F
+ */
+static int it8712f_gpio31_raise(void)
+{
+ return it87_gpio_set(32, 1);
+}
+
+/*
+ * Suited for:
+ * - ASUS P5N-D: NVIDIA MCP51 + IT8718F
+ * - ASUS P5N-E SLI: NVIDIA MCP51 + IT8718F
+ */
+static int it8718f_gpio63_raise(void)
+{
+ return it87_gpio_set(63, 1);
+}
+
+/*
+ * Suited for all boards with ambiguous DMI chassis information, which should be
+ * whitelisted because they are known to work:
+ * - ASRock IMB-A180(-H)
+ * - Intel D945GCNL
+ * - MSC Q7 Tunnel Creek Module (Q7-TCTC)
+ */
+static int p2_not_a_laptop(void)
+{
+ /* label this board as not a laptop */
+ is_laptop = 0;
+ msg_pdbg("Laptop detection overridden by P2 board enable.\n");
+ return 0;
+}
+
+/*
+ * Suited for all laptops, which are known to *not* have interfering embedded controllers.
+ */
+static int p2_whitelist_laptop(void)
+{
+ is_laptop = 1;
+ laptop_ok = 1;
+ msg_pdbg("Whitelisted laptop detected.\n");
+ return 0;
+}
+
+#endif
+
+/*
+ * Below is the list of boards which need a special "board enable" code in
+ * flashrom before their ROM chip can be accessed/written to.
+ *
+ * NOTE: Please add boards that _don't_ need such enables or don't work yet
+ * to the respective tables in print.c. Thanks!
+ *
+ * We use 2 sets of PCI IDs here, you're free to choose which is which. This
+ * is to provide a very high degree of certainty when matching a board on
+ * the basis of subsystem/card IDs. As not every vendor handles
+ * subsystem/card IDs in a sane manner.
+ *
+ * Keep the second set NULLed if it should be ignored. Keep the subsystem IDs
+ * and the dmi identifier NULLed if they don't identify the board fully to disable autodetection.
+ * But please take care to provide an as complete set of pci ids as possible;
+ * autodetection is the preferred behaviour and we would like to make sure that
+ * matches are unique.
+ *
+ * If PCI IDs are not sufficient for board matching, the match can be further
+ * constrained by a string that has to be present in the DMI database for
+ * the baseboard or the system entry. The pattern is matched by case sensitive
+ * substring match, unless it is anchored to the beginning (with a ^ in front)
+ * or the end (with a $ at the end). Both anchors may be specified at the
+ * same time to match the full field.
+ *
+ * When a board is matched through DMI, the first and second main PCI IDs
+ * and the first subsystem PCI ID have to match as well. If you specify the
+ * first subsystem ID as 0x0:0x0, the DMI matching code expects that the
+ * subsystem ID of that device is indeed zero.
+ *
+ * The coreboot ids are used two fold. When running with a coreboot firmware,
+ * the ids uniquely matches the coreboot board identification string. When a
+ * legacy bios is installed and when autodetection is not possible, these ids
+ * can be used to identify the board through the -p internal:mainboard=
+ * programmer parameter.
+ *
+ * When a board is identified through its coreboot ids (in both cases), the
+ * main pci ids are still required to match, as a safeguard.
+ */
+
+/* Please keep this list alphabetically ordered by vendor/board name. */
+const struct board_match board_matches[] = {
+
+ /* first pci-id set [4], second pci-id set [4], dmi identifier, coreboot id [2], phase, vendor name, board name max_rom_... OK? flash enable */
+#if defined(__i386__) || defined(__x86_64__)
+ {0x10DE, 0x0547, 0x147B, 0x1C2F, 0x10DE, 0x0548, 0x147B, 0x1C2F, NULL, NULL, NULL, P3, "abit", "AN-M2", 0, NT, nvidia_mcp_gpio2_raise},
+ {0x1106, 0x0282, 0x147B, 0x1415, 0x1106, 0x3227, 0x147B, 0x1415, "^AV8 ", NULL, NULL, P3, "abit", "AV8", 0, OK, board_abit_av8},
+ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, NULL /* "^I440BX-W977$" */, "abit", "bf6", P3, "abit", "BF6", 0, OK, intel_piix4_gpo26_lower},
+ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, "^i440BX-W977 (BM6)$", NULL, NULL, P3, "abit", "BM6", 0, OK, intel_piix4_gpo26_lower},
+ {0x8086, 0x24d3, 0x147b, 0x1014, 0x8086, 0x2578, 0x147b, 0x1014, NULL, NULL, NULL, P3, "abit", "IC7", 0, NT, intel_ich_gpio23_raise},
+ {0x8086, 0x2930, 0x147b, 0x1084, 0x11ab, 0x4364, 0x147b, 0x1084, NULL, NULL, NULL, P3, "abit", "IP35", 0, OK, intel_ich_gpio16_raise},
+ {0x8086, 0x2930, 0x147b, 0x1083, 0x10ec, 0x8167, 0x147b, 0x1083, NULL, NULL, NULL, P3, "abit", "IP35 Pro", 0, OK, intel_ich_gpio16_raise},
+ {0x10de, 0x0050, 0x147b, 0x1c1a, 0x10de, 0x0052, 0x147b, 0x1c1a, NULL, NULL, NULL, P3, "abit", "KN8 Ultra", 0, NT, nvidia_mcp_gpio2_lower},
+ {0x10de, 0x0369, 0x147b, 0x1c20, 0x10de, 0x0360, 0x147b, 0x1c20, "^KN9(NF-MCP55 series)$", NULL, NULL, P3, "abit", "KN9 Ultra", 0, OK, nvidia_mcp_gpio2_lower},
+ {0x10de, 0x01e0, 0x147b, 0x1c00, 0x10de, 0x0060, 0x147B, 0x1c00, NULL, NULL, NULL, P3, "abit", "NF7-S", 0, OK, nvidia_mcp_gpio8_raise},
+ {0x10de, 0x02f0, 0x147b, 0x1c26, 0x10de, 0x0260, 0x147b, 0x1c26, NULL, NULL, NULL, P3, "abit", "NF-M2 nView", 0, OK, nvidia_mcp_gpio4_lower},
+ {0x1106, 0x0691, 0, 0, 0x1106, 0x3057, 0, 0, "(VA6)$", NULL, NULL, P3, "abit", "VA6", 0, OK, via_apollo_gpo4_lower},
+ {0x1106, 0x0691, 0, 0, 0x1106, 0x3057, 0, 0, NULL, "abit", "vt6x4", P3, "abit", "VT6X4", 0, OK, via_apollo_gpo4_lower},
+ {0x105a, 0x0d30, 0x105a, 0x4d33, 0x8086, 0x1130, 0x8086, 0, NULL, NULL, NULL, P3, "Acorp", "6A815EPD", 0, OK, board_acorp_6a815epd},
+ {0x1022, 0x746B, 0, 0, 0x1022, 0x7460, 0, 0, NULL, "AGAMI", "ARUMA", P3, "agami", "Aruma", 0, OK, w83627hf_gpio24_raise_2e},
+ {0x1106, 0x3177, 0x17F2, 0x3177, 0x1106, 0x3148, 0x17F2, 0x3148, NULL, NULL, NULL, P3, "Albatron", "PM266A Pro", 0, OK, w836xx_memw_enable_2e},
+ {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, NULL, "artecgroup", "dbe61", P3, "Artec Group", "DBE61", 0, OK, board_artecgroup_dbe6x},
+ {0x1022, 0x2090, 0, 0, 0x1022, 0x2080, 0, 0, NULL, "artecgroup", "dbe62", P3, "Artec Group", "DBE62", 0, OK, board_artecgroup_dbe6x},
+ {0x8086, 0x27b9, 0xa0a0, 0x0632, 0x8086, 0x27da, 0xa0a0, 0x0632, NULL, NULL, NULL, P3, "AOpen", "i945GMx-VFX", 0, OK, intel_ich_gpio38_raise},
+ {0x8086, 0x277c, 0xa0a0, 0x060b, 0x8086, 0x27da, 0xa0a0, 0x060b, NULL, NULL, NULL, P3, "AOpen", "i975Xa-YDG", 0, OK, board_aopen_i975xa_ydg},
+ {0x8086, 0x27A0, 0x8086, 0x7270, 0x8086, 0x27B9, 0x8086, 0x7270, "^MacBook2,1$", NULL, NULL, P2, "Apple", "MacBook2,1", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x27b8, 0x1849, 0x27b8, 0x8086, 0x27da, 0x1849, 0x27da, "^ConRoeXFire-eSATA2", NULL, NULL, P3, "ASRock", "ConRoeXFire-eSATA2", 0, OK, intel_ich_gpio16_raise},
+ {0x1022, 0x1536, 0x1849, 0x1536, 0x1022, 0x780e, 0x1849, 0x780e, "^Kabini CRB$", NULL, NULL, P2, "ASRock", "IMB-A180(-H)", 0, OK, p2_not_a_laptop},
+ {0x1039, 0x0741, 0x1849, 0x0741, 0x1039, 0x5513, 0x1849, 0x5513, "^K7S41 $", NULL, NULL, P3, "ASRock", "K7S41", 0, OK, w836xx_memw_enable_2e},
+ {0x1039, 0x0741, 0x1849, 0x0741, 0x1039, 0x5513, 0x1849, 0x5513, "^K7S41GX$", NULL, NULL, P3, "ASRock", "K7S41GX", 0, OK, w836xx_memw_enable_2e},
+ {0x8086, 0x24D4, 0x1849, 0x24D0, 0x8086, 0x24D5, 0x1849, 0x9739, NULL, NULL, NULL, P3, "ASRock", "P4i65GV", 0, OK, intel_ich_gpio23_raise},
+ {0x8086, 0x2570, 0x1849, 0x2570, 0x8086, 0x24d3, 0x1849, 0x24d0, NULL, NULL, NULL, P3, "ASRock", "775i65G", 0, OK, intel_ich_gpio23_raise},
+ {0x10DE, 0x0060, 0x1043, 0x80AD, 0x10DE, 0x01E0, 0x1043, 0x80C0, NULL, NULL, NULL, P3, "ASUS", "A7N8X-VM/400", 0, OK, it8712f_gpio12_raise},
+ {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3065, 0x1043, 0x80ED, NULL, NULL, NULL, P3, "ASUS", "A7V600-X", 0, OK, it8712f_gpio31_raise},
+ {0x1106, 0x3177, 0x1043, 0x80F9, 0x1106, 0x3205, 0x1043, 0x80F9, NULL, NULL, NULL, P3, "ASUS", "A7V8X-MX", 0, OK, w836xx_memw_enable_2e},
+ {0x1106, 0x3177, 0x1043, 0x80A1, 0x1106, 0x3205, 0x1043, 0x8118, NULL, NULL, NULL, P3, "ASUS", "A7V8X-MX SE", 0, OK, w836xx_memw_enable_2e},
+ {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3177, 0x1043, 0x808C, NULL, NULL, NULL, P3, "ASUS", "A7V8X", 0, OK, it8703f_gpio51_raise},
+ {0x1106, 0x3099, 0x1043, 0x807F, 0x1106, 0x3147, 0x1043, 0x808C, NULL, NULL, NULL, P3, "ASUS", "A7V333", 0, OK, it8703f_gpio51_raise},
+ {0x1106, 0x3189, 0x1043, 0x807F, 0x1106, 0x3177, 0x1043, 0x80A1, NULL, NULL, NULL, P3, "ASUS", "A7V8X-X", 0, OK, it8712f_gpio31_raise},
+ {0x1002, 0x4372, 0x103c, 0x2a26, 0x1002, 0x4377, 0x103c, 0x2a26, NULL, NULL, NULL, P3, "ASUS", "A8AE-LE", 0, OK, amd_sbxxx_gpio9_raise},
+ {0x8086, 0x27A0, 0x1043, 0x1287, 0x8086, 0x27DF, 0x1043, 0x1287, "^A8J", NULL, NULL, P3, "ASUS", "A8Jm", 0, NT, intel_ich_gpio34_raise},
+ {0x10DE, 0x0260, 0x103C, 0x2A34, 0x10DE, 0x0264, 0x103C, 0x2A34, "NODUSM3", NULL, NULL, P3, "ASUS", "A8M2N-LA (NodusM3-GL8E)", 0, OK, nvidia_mcp_gpio0_raise},
+ {0x10DE, 0x0260, 0x103c, 0x2a3e, 0x10DE, 0x0264, 0x103c, 0x2a3e, "NAGAMI2L", NULL, NULL, P3, "ASUS", "A8N-LA (Nagami-GL8E)", 0, OK, nvidia_mcp_gpio0_raise},
+ {0x10de, 0x0264, 0x1043, 0x81bc, 0x10de, 0x02f0, 0x1043, 0x81cd, NULL, NULL, NULL, P3, "ASUS", "A8N-VM CSM", 0, OK, w83627ehf_gpio22_raise_2e},
+ {0x8086, 0x65c0, 0x1043, 0x8301, 0x8086, 0x2916, 0x1043, 0x82a6, "^DSAN-DX$", NULL, NULL, P3, "ASUS", "DSAN-DX", 0, NT, intel_ich_gpio32_raise},
+ {0x10DE, 0x0264, 0x1043, 0x81C0, 0x10DE, 0x0260, 0x1043, 0x81C0, NULL, NULL, NULL, P3, "ASUS", "M2NBP-VM CSM", 0, OK, nvidia_mcp_gpio0_raise},
+ {0x1106, 0x1336, 0x1043, 0x80ed, 0x1106, 0x3288, 0x1043, 0x8249, NULL, NULL, NULL, P3, "ASUS", "M2V-MX", 0, OK, via_vt823x_gpio5_raise},
+ {0x8086, 0x24cc, 0, 0, 0x8086, 0x24c3, 0x1043, 0x1869, "^M6Ne$", NULL, NULL, P3, "ASUS", "M6Ne", 0, NT, intel_ich_gpio43_raise},
+ {0x8086, 0x7180, 0, 0, 0x8086, 0x7110, 0, 0, "^OPLX-M$", NULL, NULL, P3, "ASUS", "OPLX-M", 0, NT, intel_piix4_gpo18_lower},
+ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, "^P2B-N$", NULL, NULL, P3, "ASUS", "P2B-N", 0, OK, intel_piix4_gpo18_lower},
+ {0x8086, 0x1A30, 0x1043, 0x8025, 0x8086, 0x244B, 0x104D, 0x80F0, NULL, NULL, NULL, P3, "ASUS", "P4B266-LM", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x1a30, 0x1043, 0x8070, 0x8086, 0x244b, 0x1043, 0x8028, NULL, NULL, NULL, P3, "ASUS", "P4B266", 0, OK, intel_ich_gpio22_raise},
+ {0x8086, 0x1A30, 0x1043, 0x8088, 0x8086, 0x24C3, 0x1043, 0x8089, NULL, NULL, NULL, P3, "ASUS", "P4B533-E", 0, NT, intel_ich_gpio22_raise},
+ {0x8086, 0x2560, 0x103C, 0x2A00, 0x8086, 0x24C3, 0x103C, 0x2A01, "^Guppy", NULL, NULL, P3, "ASUS", "P4GV-LA (Guppy)", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x24D3, 0x1043, 0x80A6, 0x8086, 0x2578, 0x1043, 0x80F6, NULL, NULL, NULL, P3, "ASUS", "P4C800-E Deluxe", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2570, 0x1043, 0x80F2, 0x8086, 0x24D5, 0x1043, 0x80F3, NULL, NULL, NULL, P3, "ASUS", "P4P800", 0, NT, intel_ich_gpio21_raise},
+ {0x8086, 0x2570, 0x1043, 0x80f2, 0x8086, 0x24d3, 0x1043, 0x80a6, "^P4P800-E$", NULL, NULL, P3, "ASUS", "P4P800-E Deluxe", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2570, 0x1043, 0x80a5, 0x8086, 0x24d3, 0x1043, 0x80a6, "^P4P800-VM$", NULL, NULL, P3, "ASUS", "P4P800-VM", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2570, 0x1043, 0x80f2, 0x8086, 0x24d3, 0x1043, 0x80a6, "^P4P800-X$", NULL, NULL, P3, "ASUS", "P4P800-X", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2570, 0x1043, 0x80b2, 0x8086, 0x24c3, 0x1043, 0x8089, "^P4PE-X/TE$",NULL, NULL, P3, "ASUS", "P4PE-X/TE", 0, NT, intel_ich_gpio21_raise},
+ {0x1039, 0x0651, 0x1043, 0x8081, 0x1039, 0x0962, 0, 0, NULL, NULL, NULL, P3, "ASUS", "P4SC-E", 0, OK, it8707f_write_enable_2e},
+ {0x8086, 0x2570, 0x1043, 0x80A5, 0x105A, 0x24D3, 0x1043, 0x80A6, NULL, NULL, NULL, P3, "ASUS", "P4SD-LA", 0, NT, intel_ich_gpio32_raise},
+ {0x1039, 0x0661, 0x1043, 0x8113, 0x1039, 0x5513, 0x1043, 0x8087, NULL, NULL, NULL, P3, "ASUS", "P4S800-MX", 512, OK, w836xx_memw_enable_2e},
+ {0x10B9, 0x1541, 0, 0, 0x10B9, 0x1533, 0, 0, "^P5A$", "asus", "p5a", P3, "ASUS", "P5A", 0, OK, board_asus_p5a},
+ {0x8086, 0x27b8, 0x1043, 0x819e, 0x8086, 0x29f0, 0x1043, 0x82a5, "^P5BV-R$", NULL, NULL, P3, "ASUS", "P5BV-R", 0, OK, intel_ich_gpio20_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x814e, "^P5GD1 PRO$", NULL, NULL, P3, "ASUS", "P5GD1 Pro", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x814e, "^P5GD1-VM$", NULL, NULL, P3, "ASUS", "P5GD1-VM/S", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x814e, NULL, NULL, NULL, P3, "ASUS", "P5GD1(-VM)", 0, NT, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x813d, "^P5GD2-Premium$", NULL, NULL, P3, "ASUS", "P5GD2 Premium", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x81a6, "^P5GD2-X$", NULL, NULL, P3, "ASUS", "P5GD2-X", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x813d, "^P5GDC-V$", NULL, NULL, P3, "ASUS", "P5GDC-V Deluxe", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x813d, "^P5GDC$", NULL, NULL, P3, "ASUS", "P5GDC Deluxe", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x266a, 0x1043, 0x80a6, 0x8086, 0x2668, 0x1043, 0x813d, NULL, NULL, NULL, P3, "ASUS", "P5GD2/C variants", 0, NT, intel_ich_gpio21_raise},
+ {0x8086, 0x27b8, 0x103c, 0x2a22, 0x8086, 0x2770, 0x103c, 0x2a22, "^LITHIUM$", NULL, NULL, P3, "ASUS", "P5LP-LE (Lithium-UL8E)",0, OK, intel_ich_gpio34_raise},
+ {0x8086, 0x27b8, 0x1043, 0x2a22, 0x8086, 0x2770, 0x1043, 0x2a22, "^P5LP-LE$", NULL, NULL, P3, "ASUS", "P5LP-LE (Epson OEM)", 0, OK, intel_ich_gpio34_raise},
+ {0x8086, 0x27da, 0x1043, 0x8179, 0x8086, 0x27b8, 0x1043, 0x8179, "^P5LD2$", NULL, NULL, P3, "ASUS", "P5LD2", 0, OK, intel_ich_gpio16_raise},
+ {0x8086, 0x27da, 0x1043, 0x8179, 0x8086, 0x27b0, 0x1043, 0x8179, "^P5LD2-MQ$", NULL, NULL, P3, "ASUS", "P5LD2-MQ", 0, OK, intel_ich_gpio16_raise},
+ {0x8086, 0x27da, 0x1043, 0x8179, 0x8086, 0x27b8, 0x1043, 0x8179, "^P5LD2-VM$", NULL, NULL, P3, "ASUS", "P5LD2-VM", 0, OK, intel_ich_gpio16_raise},
+ {0x8086, 0x27b0, 0x1043, 0x8179, 0x8086, 0x2770, 0x1043, 0x817a, "^P5LD2-VM DH$", NULL, NULL, P3, "ASUS", "P5LD2-VM DH", 0, OK, intel_ich_gpio16_raise},
+ {0x10DE, 0x0030, 0x1043, 0x818a, 0x8086, 0x100E, 0x1043, 0x80EE, NULL, NULL, NULL, P3, "ASUS", "P5ND2-SLI Deluxe", 0, OK, nvidia_mcp_gpio10_raise},
+ {0x10DE, 0x0260, 0x1043, 0x81BC, 0x10DE, 0x026C, 0x1043, 0x829E, "^P5N-D$", NULL, NULL, P3, "ASUS", "P5N-D", 0, OK, it8718f_gpio63_raise},
+ {0x10DE, 0x0260, 0x1043, 0x81BC, 0x10DE, 0x026C, 0x1043, 0x8249, "^P5N-E SLI$",NULL, NULL, P3, "ASUS", "P5N-E SLI", 0, NT, it8718f_gpio63_raise},
+ {0x8086, 0x24dd, 0x1043, 0x80a6, 0x8086, 0x2570, 0x1043, 0x8157, NULL, NULL, NULL, P3, "ASUS", "P5PE-VM", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2443, 0x1043, 0x8027, 0x8086, 0x1130, 0x1043, 0x8027, "^CUSL2-C", NULL, NULL, P3, "ASUS", "CUSL2-C", 0, OK, intel_ich_gpio21_raise},
+ {0x8086, 0x2443, 0x1043, 0x8027, 0x8086, 0x1130, 0x1043, 0x8027, "^TUSL2-C", NULL, NULL, P3, "ASUS", "TUSL2-C", 0, NT, intel_ich_gpio21_raise},
+ {0x1022, 0x780E, 0x1043, 0x1437, 0x1022, 0x780B, 0x1043, 0x1437, "^U38N$", NULL, NULL, P2, "ASUS", "U38N", 0, OK, p2_whitelist_laptop},
+ {0x1106, 0x3059, 0x1106, 0x4161, 0x1106, 0x3065, 0x1106, 0x0102, NULL, NULL, NULL, P3, "Bcom/Clientron", "WinNET P680", 0, OK, w836xx_memw_enable_2e},
+ {0x1106, 0x3177, 0x1106, 0x3177, 0x1106, 0x3116, 0x1106, 0x3116, "^KM266-8235$", "biostar", "m7viq", P3, "Biostar", "M7VIQ", 0, NT, w83697xx_memw_enable_2e},
+ {0x8086, 0x283e, 0x1028, 0x01f9, 0x8086, 0x2a01, 0, 0, "^Latitude D630", NULL, NULL, P2, "Dell", "Latitude D630", 0, OK, p2_whitelist_laptop},
+ {0x10b7, 0x9055, 0x1028, 0x0082, 0x8086, 0x7190, 0, 0, NULL, NULL, NULL, P3, "Dell", "OptiPlex GX1", 0, OK, intel_piix4_gpo30_lower},
+ {0x8086, 0x3590, 0x1028, 0x016c, 0x1000, 0x0030, 0x1028, 0x016c, NULL, NULL, NULL, P3, "Dell", "PowerEdge 1850", 0, OK, intel_ich_gpio23_raise},
+ {0x1106, 0x3189, 0x1106, 0x3189, 0x1106, 0x3177, 0x1106, 0x3177, "^AD77", "dfi", "ad77", P3, "DFI", "AD77", 0, NT, w836xx_memw_enable_2e},
+ {0x1039, 0x6325, 0x1019, 0x0f05, 0x1039, 0x0016, 0, 0, NULL, NULL, NULL, P2, "Elitegroup", "A928", 0, OK, p2_whitelist_laptop},
+ {0x10de, 0x03ea, 0x1019, 0x2602, 0x10de, 0x03e0, 0x1019, 0x2602, NULL, NULL, NULL, P3, "Elitegroup", "GeForce6100SM-M", 0, OK, board_ecs_geforce6100sm_m},
+ {0x1106, 0x3038, 0x1019, 0x0996, 0x1106, 0x3177, 0x1019, 0x0996, NULL, NULL, NULL, P3, "Elitegroup", "K7VTA3", 256, OK, NULL},
+ {0x1106, 0x3177, 0x1106, 0x3177, 0x1106, 0x3059, 0x1695, 0x3005, NULL, NULL, NULL, P3, "EPoX", "EP-8K5A2", 0, OK, w836xx_memw_enable_2e},
+ {0x10DE, 0x005E, 0x1695, 0x1010, 0x10DE, 0x0050, 0x1695, 0x1010, "8NPA7I", NULL, NULL, P3, "EPoX", "EP-8NPA7I", 0, OK, nvidia_mcp_gpio4_raise},
+ {0x10DE, 0x005E, 0x1695, 0x1010, 0x10DE, 0x0050, 0x1695, 0x1010, "9NPA7I", NULL, NULL, P3, "EPoX", "EP-9NPA7I", 0, OK, nvidia_mcp_gpio4_raise},
+ {0x10EC, 0x8139, 0x1695, 0x9001, 0x11C1, 0x5811, 0x1695, 0x9015, NULL, NULL, NULL, P3, "EPoX", "EP-8RDA3+", 0, OK, nvidia_mcp_gpio31_raise},
+ {0x8086, 0x7110, 0, 0, 0x8086, 0x7190, 0, 0, NULL, "epox", "ep-bx3", P3, "EPoX", "EP-BX3", 0, NT, intel_piix4_gpo22_raise},
+ {0x10de, 0x02f0, 0x105b, 0x0d01, 0x10de, 0x0264, 0x105b, 0x0d01, NULL, NULL, NULL, P3, "Foxconn", "6150K8MD-8EKRSH", 0, NT, nvidia_mcp_gpio2_raise},
+ {0x8086, 0x2A40, 0x1734, 0x1148, 0x8086, 0x2930, 0x1734, 0x1148, "^XY680", NULL, NULL, P2, "Fujitsu", "Amilo Xi 3650", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x2443, 0x8086, 0x2442, 0x8086, 0x1130, 0x8086, 0x1130, "^6IEM ", NULL, NULL, P3, "GIGABYTE", "GA-6IEM", 0, NT, intel_ich_gpio25_raise},
+ {0x1106, 0x0686, 0x1106, 0x0686, 0x1106, 0x3058, 0x1458, 0xa000, NULL, NULL, NULL, P3, "GIGABYTE", "GA-7ZM", 512, OK, NULL},
+ {0x8086, 0x2570, 0x1458, 0x2570, 0x8086, 0x24d0, 0, 0, "^8IP775/-G$",NULL, NULL, P3, "GIGABYTE", "GA-8IP775", 0, OK, intel_ich_gpio32_raise},
+ {0x8086, 0x244b, 0x8086, 0x2442, 0x8086, 0x2445, 0x1458, 0xa002, NULL, NULL, NULL, P3, "GIGABYTE", "GA-8IRML", 0, OK, intel_ich_gpio25_raise},
+ {0x8086, 0x24c3, 0x1458, 0x24c2, 0x8086, 0x24cd, 0x1458, 0x5004, NULL, NULL, NULL, P3, "GIGABYTE", "GA-8PE667 Ultra 2", 0, OK, intel_ich_gpio32_raise},
+ {0x1039, 0x0650, 0x1039, 0x0650, 0x1039, 0x7012, 0x1458, 0xA002, "^GA-8SIMLFS20$", NULL, NULL, P3, "GIGABYTE", "GA-8SIMLFS 2.0", 0, OK, sis_gpio0_raise_and_w836xx_memw},
+ {0x1039, 0x0651, 0x1039, 0x0651, 0x1039, 0x7002, 0x1458, 0x5004, "^GA-8SIMLH$",NULL, NULL, P3, "GIGABYTE", "GA-8SIMLH", 0, OK, sis_gpio0_raise_and_w836xx_memw},
+ {0x10DE, 0x02F1, 0x1458, 0x5000, 0x10DE, 0x0261, 0x1458, 0x5001, NULL, NULL, NULL, P3, "GIGABYTE", "GA-K8N51GMF", 0, OK, nvidia_mcp_gpio3b_raise},
+ {0x10DE, 0x026C, 0x1458, 0xA102, 0x10DE, 0x0260, 0x1458, 0x5001, NULL, NULL, NULL, P3, "GIGABYTE", "GA-K8N51GMF-9", 0, OK, nvidia_mcp_gpio3b_raise},
+ {0x10DE, 0x00E4, 0x1458, 0x0C11, 0x10DE, 0x00E0, 0x1458, 0x0C11, NULL, NULL, NULL, P3, "GIGABYTE", "GA-K8NS", 0, OK, nvidia_mcp_gpio0a_raise},
+ {0x10DE, 0x0050, 0x1458, 0x0C11, 0x10DE, 0x005e, 0x1458, 0x5000, NULL, NULL, NULL, P3, "GIGABYTE", "GA-K8N-SLI", 0, OK, nvidia_mcp_gpio21_raise},
+ {0x8086, 0x2415, 0x103c, 0x1250, 0x10b7, 0x9200, 0x103c, 0x1247, NULL, NULL, NULL, P3, "HP", "e-Vectra P2706T", 0, OK, board_hp_p2706t},
+ {0x1166, 0x0223, 0x103c, 0x320d, 0x14e4, 0x1678, 0x103c, 0x703e, NULL, "hp", "dl145_g3", P3, "HP", "ProLiant DL145 G3", 0, OK, board_hp_dl145_g3_enable},
+ {0x1166, 0x0223, 0x103c, 0x320d, 0x14e4, 0x1648, 0x103c, 0x310f, NULL, "hp", "dl165_g6", P3, "HP", "ProLiant DL165 G6", 0, OK, board_hp_dl165_g6_enable},
+ {0x8086, 0x2580, 0x103c, 0x2a08, 0x8086, 0x2640, 0x103c, 0x2a0a, NULL, NULL, NULL, P3, "HP", "Puffer2-UL8E", 0, OK, intel_ich_gpio18_raise},
+ {0x8086, 0x2415, 0x103c, 0x1249, 0x10b7, 0x9200, 0x103c, 0x1246, NULL, NULL, NULL, P3, "HP", "Vectra VL400", 0, OK, board_hp_vl400},
+ {0x8086, 0x1a30, 0x103c, 0x1a30, 0x8086, 0x2443, 0x103c, 0x2440, "^VL420$", NULL, NULL, P3, "HP", "Vectra VL420 SFF", 0, OK, intel_ich_gpio22_raise},
+ {0x10de, 0x0369, 0x103c, 0x12fe, 0x10de, 0x0364, 0x103c, 0x12fe, NULL, "hp", "xw9400", P3, "HP", "xw9400", 0, OK, nvidia_mcp_gpio5_raise},
+ {0x8086, 0x27A0, 0, 0, 0x8086, 0x27B9, 0, 0, NULL, "ibase", "mb899", P3, "IBASE", "MB899", 0, OK, intel_ich_gpio26_raise},
+ {0x1166, 0x0205, 0x1014, 0x0347, 0x1002, 0x515E, 0x1014, 0x0325, NULL, NULL, NULL, P3, "IBM", "x3455", 0, OK, board_ibm_x3455},
+ {0x1039, 0x5513, 0x8086, 0xd61f, 0x1039, 0x6330, 0x8086, 0xd61f, NULL, NULL, NULL, P3, "Intel", "D201GLY", 0, OK, wbsio_check_for_spi},
+ {0x8086, 0x27b8, 0x8086, 0xd606, 0x8086, 0x2770, 0x8086, 0xd606, "^D945GCNL$", NULL, NULL, P2, "Intel", "D945GCNL", 0, OK, p2_not_a_laptop},
+ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, "^SE440BX-2$", NULL, NULL, P3, "Intel", "SE440BX-2", 0, NT, intel_piix4_gpo27_lower},
+ {0x1022, 0x7468, 0, 0, 0x1022, 0x7460, 0, 0, NULL, "iwill", "dk8_htx", P3, "IWILL", "DK8-HTX", 0, OK, w83627hf_gpio24_raise_2e},
+ {0x8086, 0x27A0, 0x8086, 0x27a0, 0x8086, 0x27b8, 0x8086, 0x27b8, NULL, "kontron", "986lcd-m", P3, "Kontron", "986LCD-M", 0, OK, board_kontron_986lcd_m},
+ {0x8086, 0x2917, 0x17AA, 0x20F5, 0x8086, 0x2930, 0x17AA, 0x20F9, "^ThinkPad T400", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad T400", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x1E22, 0x17AA, 0x21F6, 0x8086, 0x1E55, 0x17AA, 0x21F6, "^ThinkPad T530", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad T530", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x27a0, 0x17aa, 0x2015, 0x8086, 0x27b9, 0x17aa, 0x2009, "^ThinkPad T60", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad T60", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x27a0, 0x17aa, 0x2017, 0x8086, 0x27b9, 0x17aa, 0x2009, "^ThinkPad T60", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad T60(s)", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x2917, 0x17AA, 0x20F5, 0x8086, 0x2930, 0x17AA, 0x20F9, "^ThinkPad X200", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad X200", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x3B07, 0x17AA, 0x2166, 0x8086, 0x3B30, 0x17AA, 0x2167, "^Lenovo X201", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad X201", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x1E22, 0x17AA, 0x21FA, 0x8086, 0x1E55, 0x17AA, 0x21FA, "^ThinkPad X230", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad X230", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x27A0, 0x17AA, 0x2017, 0x8086, 0x27B9, 0x17AA, 0x2009, "^ThinkPad X60", NULL, NULL, P2, "IBM/Lenovo", "ThinkPad X60(s)", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x2411, 0x8086, 0x2411, 0x8086, 0x7125, 0x0e11, 0xb165, NULL, NULL, NULL, P3, "Mitac", "6513WU", 0, OK, board_mitac_6513wu},
+ {0x8086, 0x8186, 0x8086, 0x8186, 0x8086, 0x8800, 0, 0, "^MSC Vertriebs GmbH$", NULL, NULL, P2, "MSC", "Q7-TCTC", 0, OK, p2_not_a_laptop},
+ {0x8086, 0x7190, 0, 0, 0x8086, 0x7110, 0, 0, "^MS-6163 (i440BX)$", NULL, NULL, P3, "MSI", "MS-6163 (MS-6163 Pro)", 0, OK, intel_piix4_gpo14_raise},
+ {0x8086, 0x244b, 0x1462, 0x3910, 0x8086, 0x2442, 0x1462, 0x3910, NULL, NULL, NULL, P3, "MSI", "MS-6391 (845 Pro4)", 0, OK, intel_ich_gpio23_raise},
+ {0x1039, 0x0745, 0, 0, 0x1039, 0x0018, 0, 0, "^MS-6561", NULL, NULL, P3, "MSI", "MS-6561 (745 Ultra)", 0, OK, w836xx_memw_enable_2e},
+ {0x8086, 0x2560, 0x1462, 0x5770, 0x8086, 0x24C3, 0x1462, 0x5770, NULL, NULL, NULL, P3, "MSI", "MS-6577 (Xenon)", 0, OK, w83627hf_gpio25_raise_2e},
+ {0x13f6, 0x0111, 0x1462, 0x5900, 0x1106, 0x3177, 0x1106, 0, NULL, NULL, NULL, P3, "MSI", "MS-6590 (KT4 Ultra)", 0, OK, board_msi_kt4v},
+ {0x1106, 0x0282, 0x1106, 0x0282, 0x1106, 0x3227, 0x1106, 0x3227, "^MS-7094$", NULL, NULL, P3, "MSI", "MS-7094 (K8T Neo2-F V2.0)", 0, OK, w83627thf_gpio44_raise_2e},
+ {0x1106, 0x0571, 0x1462, 0x7120, 0x1106, 0x3065, 0x1462, 0x7120, NULL, NULL, NULL, P3, "MSI", "MS-6712 (KT4V)", 0, OK, board_msi_kt4v},
+ {0x1106, 0x3148, 0 , 0 , 0x1106, 0x3177, 0 , 0 , NULL, "msi", "ms6787", P3, "MSI", "MS-6787 (P4MAM-V/P4MAM-L)", 0, OK, w836xx_memw_enable_2e},
+ {0x8086, 0x24d3, 0x1462, 0x7880, 0x8086, 0x2570, 0, 0, NULL, NULL, NULL, P3, "MSI", "MS-6788-040 (848P NeoV)", 0, OK, intel_ich_gpio32_raise},
+ {0x1039, 0x7012, 0x1462, 0x0050, 0x1039, 0x6325, 0x1462, 0x0058, NULL, NULL, NULL, P3, "MSI", "MS-7005 (651M-L)", 0, OK, sis_gpio0_raise_and_w836xx_memw},
+ {0x10DE, 0x00E0, 0x1462, 0x0250, 0x10DE, 0x00E1, 0x1462, 0x0250, NULL, NULL, NULL, P3, "MSI", "MS-7025 (K8N Neo2 Platinum)", 0, OK, nvidia_mcp_gpio0c_raise},
+ {0x10DE, 0x00E0, 0x1462, 0x0300, 0x10DE, 0x00E1, 0x1462, 0x0300, NULL, NULL, NULL, P3, "MSI", "MS-7030 (K8N Neo Platinum)", 0, OK, nvidia_mcp_gpio0c_raise},
+ {0x8086, 0x2658, 0x1462, 0x7046, 0x1106, 0x3044, 0x1462, 0x046d, NULL, NULL, NULL, P3, "MSI", "MS-7046", 0, OK, intel_ich_gpio19_raise},
+ {0x1106, 0x3149, 0x1462, 0x7061, 0x1106, 0x3227, 0, 0, NULL, NULL, NULL, P3, "MSI", "MS-7061 (KM4M-V/KM4AM-V)", 0, OK, w836xx_memw_enable_2e},
+ {0x10DE, 0x005E, 0x1462, 0x7125, 0x10DE, 0x0052, 0x1462, 0x7125, NULL, NULL, NULL, P3, "MSI", "MS-7125 (K8N Neo4(-F/-FI/-FX/Platinum))", 0, OK, nvidia_mcp_gpio2_raise},
+ {0x10DE, 0x005E, 0x1462, 0x7135, 0x10DE, 0x0050, 0x1462, 0x7135, NULL, "msi", "k8n-neo3", P3, "MSI", "MS-7135 (K8N Neo3)", 0, OK, w83627thf_gpio44_raise_4e},
+ {0x10DE, 0x0270, 0x1462, 0x7207, 0x10DE, 0x0264, 0x1462, 0x7207, NULL, NULL, NULL, P3, "MSI", "MS-7207 (K8NGM2-L)", 0, NT, nvidia_mcp_gpio2_raise},
+ {0x10DE, 0x0360, 0x1462, 0x7250, 0x10DE, 0x0368, 0x1462, 0x7250, NULL, NULL, NULL, P3, "MSI", "MS-7250 (K9N SLI)", 0, OK, nvidia_mcp_gpio2_raise},
+ {0x1011, 0x0019, 0xaa55, 0xaa55, 0x8086, 0x7190, 0, 0, NULL, NULL, NULL, P3, "Nokia", "IP530", 0, OK, fdc37b787_gpio50_raise_3f0},
+ {0x8086, 0x3B30, 0x1025, 0x0379, 0x8086, 0x3B09, 0x1025, 0x0379, "^EasyNote LM85$", NULL, NULL, P2, "Packard Bell","EasyNote LM85", 0, OK, p2_whitelist_laptop},
+ {0x8086, 0x24d3, 0x144d, 0xb025, 0x8086, 0x1050, 0x144d, 0xb025, NULL, NULL, NULL, P3, "Samsung", "Polaris 32", 0, OK, intel_ich_gpio21_raise},
+ {0x1106, 0x3099, 0, 0, 0x1106, 0x3074, 0, 0, NULL, "shuttle", "ak31", P3, "Shuttle", "AK31", 0, OK, w836xx_memw_enable_2e},
+ {0x1106, 0x3104, 0x1297, 0xa238, 0x1106, 0x3059, 0x1297, 0xc063, NULL, NULL, NULL, P3, "Shuttle", "AK38N", 256, OK, NULL},
+ {0x1106, 0x3038, 0x0925, 0x1234, 0x1106, 0x3058, 0x15DD, 0x7609, NULL, NULL, NULL, P3, "Soyo", "SY-7VCA", 0, OK, via_apollo_gpo0_lower},
+ {0x10de, 0x0364, 0x108e, 0x6676, 0x10de, 0x0369, 0x108e, 0x6676, "^Sun Ultra 40 M2", NULL, NULL, P3, "Sun", "Ultra 40 M2", 0, OK, board_sun_ultra_40_m2},
+ {0x1106, 0x3038, 0x0925, 0x1234, 0x1106, 0x0596, 0x1106, 0, NULL, NULL, NULL, P3, "Tekram", "P6Pro-A5", 256, OK, NULL},
+ {0x1106, 0x3123, 0x1106, 0x3123, 0x1106, 0x3059, 0x1106, 0x4161, NULL, NULL, NULL, P3, "Termtek", "TK-3370 (Rev:2.5B)", 0, OK, w836xx_memw_enable_4e},
+ {0x8086, 0x7120, 0x109f, 0x3157, 0x8086, 0x2410, 0, 0, NULL, NULL, NULL, P3, "TriGem", "Anaheim-3", 0, OK, intel_ich_gpio22_raise},
+ {0x8086, 0x1076, 0x8086, 0x1176, 0x1106, 0x3059, 0x10f1, 0x2498, NULL, NULL, NULL, P3, "Tyan", "S2498 (Tomcat K7M)", 0, OK, w836xx_memw_enable_2e},
+ {0x1106, 0x0259, 0x1106, 0xAA07, 0x1106, 0x3227, 0x1106, 0xAA07, NULL, NULL, NULL, P3, "VIA", "EPIA EK", 0, NT, via_vt823x_gpio9_raise},
+ {0x1106, 0x3177, 0x1106, 0xAA01, 0x1106, 0x3123, 0x1106, 0xAA01, NULL, NULL, NULL, P3, "VIA", "EPIA M/MII/...", 0, OK, via_vt823x_gpio15_raise},
+ {0x1106, 0x0259, 0x1106, 0x3227, 0x1106, 0x3065, 0x1106, 0x3149, NULL, NULL, NULL, P3, "VIA", "EPIA-N/NL", 0, OK, via_vt823x_gpio9_raise},
+#endif
+ { 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, P3, NULL, NULL, 0, NT, NULL}, /* end marker */
+};
+
+int selfcheck_board_enables(void)
+{
+ if (board_matches[ARRAY_SIZE(board_matches) - 1].vendor_name != NULL) {
+ msg_gerr("Board enables table miscompilation!\n");
+ return 1;
+ }
+
+ int ret = 0;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(board_matches) - 1; i++) {
+ const struct board_match *b = &board_matches[i];
+ if (b->vendor_name == NULL || b->board_name == NULL) {
+ msg_gerr("ERROR: Board enable #%d does not define a vendor and board name.\n"
+ "Please report a bug at flashrom@flashrom.org\n", i);
+ ret = 1;
+ continue;
+ }
+ if ((b->first_vendor == 0 || b->first_device == 0 ||
+ b->second_vendor == 0 || b->second_device == 0) ||
+ ((b->lb_vendor == NULL) ^ (b->lb_part == NULL)) ||
+ (b->max_rom_decode_parallel == 0 && b->enable == NULL)) {
+ msg_gerr("ERROR: Board enable for %s %s is misdefined.\n"
+ "Please report a bug at flashrom@flashrom.org\n",
+ b->vendor_name, b->board_name);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+/* Parse the <vendor>:<board> string specified by the user as part of -p internal:mainboard=<vendor>:<board>.
+ * Parameters vendor and model will be overwritten. Returns 0 on success.
+ * Note: strtok modifies the original string, so we work on a copy and allocate memory for the results.
+ */
+int board_parse_parameter(const char *boardstring, const char **vendor, const char **model)
+{
+ /* strtok may modify the original string. */
+ char *tempstr = strdup(boardstring);
+ char *tempstr2 = NULL;
+ strtok(tempstr, ":");
+ tempstr2 = strtok(NULL, ":");
+ if (tempstr == NULL || tempstr2 == NULL) {
+ free(tempstr);
+ msg_pinfo("Please supply the board vendor and model name with the "
+ "-p internal:mainboard=<vendor>:<model> option.\n");
+ return 1;
+ }
+ *vendor = strdup(tempstr);
+ *model = strdup(tempstr2);
+ msg_pspew("-p internal:mainboard: vendor=\"%s\", model=\"%s\"\n", tempstr, tempstr2);
+ free(tempstr);
+ return 0;
+}
+
+/*
+ * Match boards on vendor and model name.
+ * The string parameters can come either from the coreboot table or the command line (i.e. the user).
+ * The boolean needs to be set accordingly to compare them to the right entries of the board enables table.
+ * Require main PCI IDs to match too as extra safety.
+ * Parameters vendor and model must be non-NULL!
+ */
+static const struct board_match *board_match_name(const char *vendor, const char *model, bool cb)
+{
+ const struct board_match *board = board_matches;
+ const struct board_match *partmatch = NULL;
+
+ for (; board->vendor_name; board++) {
+ const char *cur_vendor = cb ? board->lb_vendor : board->vendor_name;
+ const char *cur_model = cb ? board->lb_part : board->board_name;
+
+ if (!cur_vendor || strcasecmp(cur_vendor, vendor))
+ continue;
+
+ if (!cur_model || strcasecmp(cur_model, model))
+ continue;
+
+ if (!pci_dev_find(board->first_vendor, board->first_device)) {
+ msg_pdbg("Odd. Board name \"%s\":\"%s\" matches, but first PCI device %04x:%04x "
+ "doesn't.\n", vendor, model, board->first_vendor, board->first_device);
+ continue;
+ }
+
+ if (!pci_dev_find(board->second_vendor, board->second_device)) {
+ msg_pdbg("Odd. Board name \"%s\":\"%s\" matches, but second PCI device %04x:%04x "
+ "doesn't.\n", vendor, model, board->second_vendor, board->second_device);
+ continue;
+ }
+
+ if (partmatch) {
+ /* More than one entry has a matching name. */
+ msg_perr("Board name \"%s\":\"%s\" and PCI IDs matched more than one board enable "
+ "entry. Please report a bug at flashrom@flashrom.org\n", vendor, model);
+ return NULL;
+ }
+ partmatch = board;
+ }
+
+ if (partmatch)
+ return partmatch;
+
+ return NULL;
+}
+
+/*
+ * Match boards on PCI IDs and subsystem IDs.
+ * Second set of IDs can be either main+subsystem IDs, main IDs or no IDs.
+ */
+const static struct board_match *board_match_pci_ids(enum board_match_phase phase)
+{
+ const struct board_match *board = board_matches;
+
+ for (; board->vendor_name; board++) {
+ if ((!board->first_card_vendor || !board->first_card_device) &&
+ !board->dmi_pattern)
+ continue;
+ if (board->phase != phase)
+ continue;
+
+ if (!pci_card_find(board->first_vendor, board->first_device,
+ board->first_card_vendor,
+ board->first_card_device))
+ continue;
+
+ if (board->second_vendor) {
+ if (board->second_card_vendor) {
+ if (!pci_card_find(board->second_vendor,
+ board->second_device,
+ board->second_card_vendor,
+ board->second_card_device))
+ continue;
+ } else {
+ if (!pci_dev_find(board->second_vendor,
+ board->second_device))
+ continue;
+ }
+ }
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (board->dmi_pattern) {
+ if (!has_dmi_support) {
+ msg_pwarn("Warning: Can't autodetect %s %s, DMI info unavailable.\n",
+ board->vendor_name, board->board_name);
+ msg_pinfo("Please supply the board vendor and model name with the "
+ "-p internal:mainboard=<vendor>:<model> option.\n");
+ continue;
+ } else {
+ if (!dmi_match(board->dmi_pattern))
+ continue;
+ }
+ }
+#endif // defined(__i386__) || defined(__x86_64__)
+ return board;
+ }
+
+ return NULL;
+}
+
+static int board_enable_safetycheck(const struct board_match *board)
+{
+ if (!board)
+ return 1;
+
+ if (board->status == OK)
+ return 0;
+
+ if (!force_boardenable) {
+ msg_pwarn("Warning: The mainboard-specific code for %s %s has not been tested,\n"
+ "and thus will not be executed by default. Depending on your hardware,\n"
+ "erasing, writing or even probing can fail without running this code.\n\n"
+ "Please see the man page (section PROGRAMMER SPECIFIC INFO, subsection\n"
+ "\"internal programmer\") for details.\n", board->vendor_name, board->board_name);
+ return 1;
+ }
+ msg_pwarn("NOTE: Running an untested board enable procedure.\n"
+ "Please report success/failure to flashrom@flashrom.org.\n");
+ return 0;
+}
+
+/* FIXME: Should this be identical to board_flash_enable? */
+static int board_handle_phase(enum board_match_phase phase)
+{
+ const struct board_match *board = NULL;
+
+ board = board_match_pci_ids(phase);
+
+ if (!board)
+ return 0;
+
+ if (board_enable_safetycheck(board))
+ return 0;
+
+ if (!board->enable) {
+ /* Not sure if there is a valid case for this. */
+ msg_perr("Board match found, but nothing to do?\n");
+ return 0;
+ }
+
+ return board->enable();
+}
+
+void board_handle_before_superio(void)
+{
+ board_handle_phase(P1);
+}
+
+void board_handle_before_laptop(void)
+{
+ board_handle_phase(P2);
+}
+
+int board_flash_enable(const char *vendor, const char *model, const char *cb_vendor, const char *cb_model)
+{
+ const struct board_match *board = NULL;
+ int ret = 0;
+
+ if (vendor != NULL && model != NULL) {
+ board = board_match_name(vendor, model, false);
+ if (!board) { /* If a board was given by the user it has to match, else we abort here. */
+ msg_perr("No suitable board enable found for vendor=\"%s\", model=\"%s\".\n",
+ vendor, model);
+ return 1;
+ }
+ }
+ if (board == NULL && cb_vendor != NULL && cb_model != NULL) {
+ board = board_match_name(cb_vendor, cb_model, true);
+ if (!board) { /* Failure is an option here, because many cb boards don't require an enable. */
+ msg_pdbg2("No board enable found matching coreboot IDs vendor=\"%s\", model=\"%s\".\n",
+ cb_vendor, cb_model);
+ }
+ }
+ if (board == NULL) {
+ board = board_match_pci_ids(P3);
+ if (!board) /* i.e. there is just no board enable available for this board */
+ return 0;
+ }
+
+ if (board_enable_safetycheck(board))
+ return 1;
+
+ /* limit the maximum size of the parallel bus */
+ if (board->max_rom_decode_parallel)
+ max_rom_decode.parallel = board->max_rom_decode_parallel * 1024;
+
+ if (board->enable != NULL) {
+ msg_pinfo("Enabling full flash access for board \"%s %s\"... ",
+ board->vendor_name, board->board_name);
+
+ ret = board->enable();
+ if (ret)
+ msg_pinfo("FAILED!\n");
+ else
+ msg_pinfo("OK.\n");
+ }
+
+ return ret;
+}
diff --git a/buspirate_spi.c b/buspirate_spi.c
new file mode 100644
index 0000000..b6554ac
--- /dev/null
+++ b/buspirate_spi.c
@@ -0,0 +1,553 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009, 2010, 2011, 2012 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* Change this to #define if you want to test without a serial implementation */
+#undef FAKE_COMMUNICATION
+
+struct buspirate_spispeeds {
+ const char *name;
+ const int speed;
+};
+
+#ifndef FAKE_COMMUNICATION
+static int buspirate_serialport_setup(char *dev)
+{
+ /* 115200bps, 8 databits, no parity, 1 stopbit */
+ sp_fd = sp_openserport(dev, 115200);
+ if (sp_fd == SER_INV_FD)
+ return 1;
+ return 0;
+}
+#else
+#define buspirate_serialport_setup(...) 0
+#define serialport_shutdown(...) 0
+#define serialport_write(...) 0
+#define serialport_read(...) 0
+#define sp_flush_incoming(...) 0
+#endif
+
+static unsigned char *bp_commbuf = NULL;
+static int bp_commbufsize = 0;
+
+static int buspirate_commbuf_grow(int bufsize)
+{
+ unsigned char *tmpbuf;
+
+ /* Never shrink. realloc() calls are expensive. */
+ if (bufsize <= bp_commbufsize)
+ return 0;
+
+ tmpbuf = realloc(bp_commbuf, bufsize);
+ if (!tmpbuf) {
+ /* Keep the existing buffer because memory is already tight. */
+ msg_perr("Out of memory!\n");
+ return ERROR_OOM;
+ }
+
+ bp_commbuf = tmpbuf;
+ bp_commbufsize = bufsize;
+ return 0;
+}
+
+static int buspirate_sendrecv(unsigned char *buf, unsigned int writecnt,
+ unsigned int readcnt)
+{
+ int i, ret = 0;
+
+ msg_pspew("%s: write %i, read %i ", __func__, writecnt, readcnt);
+ if (!writecnt && !readcnt) {
+ msg_perr("Zero length command!\n");
+ return 1;
+ }
+ if (writecnt)
+ msg_pspew("Sending");
+ for (i = 0; i < writecnt; i++)
+ msg_pspew(" 0x%02x", buf[i]);
+#ifdef FAKE_COMMUNICATION
+ /* Placate the caller for now. */
+ if (readcnt) {
+ buf[0] = 0x01;
+ memset(buf + 1, 0xff, readcnt - 1);
+ }
+ ret = 0;
+#else
+ if (writecnt)
+ ret = serialport_write(buf, writecnt);
+ if (ret)
+ return ret;
+ if (readcnt)
+ ret = serialport_read(buf, readcnt);
+ if (ret)
+ return ret;
+#endif
+ if (readcnt)
+ msg_pspew(", receiving");
+ for (i = 0; i < readcnt; i++)
+ msg_pspew(" 0x%02x", buf[i]);
+ msg_pspew("\n");
+ return 0;
+}
+
+static int buspirate_wait_for_string(unsigned char *buf, char *key)
+{
+ unsigned int keylen = strlen(key);
+ int ret;
+
+ ret = buspirate_sendrecv(buf, 0, keylen);
+ while (!ret) {
+ if (!memcmp(buf, key, keylen))
+ return 0;
+ memmove(buf, buf + 1, keylen - 1);
+ ret = buspirate_sendrecv(buf + keylen - 1, 0, 1);
+ }
+ return ret;
+}
+
+static int buspirate_spi_send_command_v1(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+static int buspirate_spi_send_command_v2(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+
+static struct spi_master spi_master_buspirate = {
+ .type = SPI_CONTROLLER_BUSPIRATE,
+ .max_data_read = MAX_DATA_UNSPECIFIED,
+ .max_data_write = MAX_DATA_UNSPECIFIED,
+ .command = NULL,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static const struct buspirate_spispeeds spispeeds[] = {
+ {"30k", 0x0},
+ {"125k", 0x1},
+ {"250k", 0x2},
+ {"1M", 0x3},
+ {"2M", 0x4},
+ {"2.6M", 0x5},
+ {"4M", 0x6},
+ {"8M", 0x7},
+ {NULL, 0x0},
+};
+
+static int buspirate_spi_shutdown(void *data)
+{
+ int ret = 0, ret2 = 0;
+ /* No need to allocate a buffer here, we know that bp_commbuf is at least DEFAULT_BUFSIZE big. */
+
+ /* Exit raw SPI mode (enter raw bitbang mode) */
+ bp_commbuf[0] = 0x00;
+ if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0)))
+ goto out_shutdown;
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO")))
+ goto out_shutdown;
+ if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1)))
+ goto out_shutdown;
+ msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[0]);
+ if (bp_commbuf[0] != '1') {
+ msg_perr("Can't handle raw bitbang mode version %c!\n", bp_commbuf[0]);
+ ret = 1;
+ goto out_shutdown;
+ }
+ /* Reset Bus Pirate (return to user terminal) */
+ bp_commbuf[0] = 0x0f;
+ ret = buspirate_sendrecv(bp_commbuf, 1, 0);
+
+out_shutdown:
+ /* Shut down serial port communication */
+ ret2 = serialport_shutdown(NULL);
+ /* Keep the oldest error, it is probably the best indicator. */
+ if (ret2 && !ret)
+ ret = ret2;
+ bp_commbufsize = 0;
+ free(bp_commbuf);
+ bp_commbuf = NULL;
+ if (ret)
+ msg_pdbg("Bus Pirate shutdown failed.\n");
+ else
+ msg_pdbg("Bus Pirate shutdown completed.\n");
+
+ return ret;
+}
+
+#define BP_FWVERSION(a,b) ((a) << 8 | (b))
+
+int buspirate_spi_init(void)
+{
+ char *tmp;
+ char *dev;
+ int i;
+ unsigned int fw_version_major = 0;
+ unsigned int fw_version_minor = 0;
+ int spispeed = 0x7;
+ int ret = 0;
+ int pullup = 0;
+
+ dev = extract_programmer_param("dev");
+ if (dev && !strlen(dev)) {
+ free(dev);
+ dev = NULL;
+ }
+ if (!dev) {
+ msg_perr("No serial device given. Use flashrom -p buspirate_spi:dev=/dev/ttyUSB0\n");
+ return 1;
+ }
+
+ tmp = extract_programmer_param("spispeed");
+ if (tmp) {
+ for (i = 0; spispeeds[i].name; i++) {
+ if (!strncasecmp(spispeeds[i].name, tmp, strlen(spispeeds[i].name))) {
+ spispeed = spispeeds[i].speed;
+ break;
+ }
+ }
+ if (!spispeeds[i].name)
+ msg_perr("Invalid SPI speed, using default.\n");
+ }
+ free(tmp);
+
+ tmp = extract_programmer_param("pullups");
+ if (tmp) {
+ if (strcasecmp("on", tmp) == 0)
+ pullup = 1;
+ else if (strcasecmp("off", tmp) == 0)
+ ; // ignore
+ else
+ msg_perr("Invalid pullups state, not using them.\n");
+ }
+ free(tmp);
+
+ /* Default buffer size is 19: 16 bytes data, 3 bytes control. */
+#define DEFAULT_BUFSIZE (16 + 3)
+ bp_commbuf = malloc(DEFAULT_BUFSIZE);
+ if (!bp_commbuf) {
+ bp_commbufsize = 0;
+ msg_perr("Out of memory!\n");
+ free(dev);
+ return ERROR_OOM;
+ }
+ bp_commbufsize = DEFAULT_BUFSIZE;
+
+ ret = buspirate_serialport_setup(dev);
+ free(dev);
+ if (ret) {
+ bp_commbufsize = 0;
+ free(bp_commbuf);
+ bp_commbuf = NULL;
+ return ret;
+ }
+
+ if (register_shutdown(buspirate_spi_shutdown, NULL) != 0) {
+ bp_commbufsize = 0;
+ free(bp_commbuf);
+ bp_commbuf = NULL;
+ return 1;
+ }
+
+ /* This is the brute force version, but it should work.
+ * It is likely to fail if a previous flashrom run was aborted during a write with the new SPI commands
+ * in firmware v5.5 because that firmware may wait for up to 4096 bytes of input before responding to
+ * 0x00 again. The obvious workaround (sending 4096 bytes of \0) may cause significant startup delays.
+ */
+ for (i = 0; i < 20; i++) {
+ /* Enter raw bitbang mode */
+ bp_commbuf[0] = 0x00;
+ /* Send the command, don't read the response. */
+ ret = buspirate_sendrecv(bp_commbuf, 1, 0);
+ if (ret)
+ return ret;
+ /* The old way to handle responses from a Bus Pirate already in BBIO mode was to flush any
+ * response which came in over serial. Unfortunately that does not work reliably on Linux
+ * with FTDI USB-serial.
+ */
+ //sp_flush_incoming();
+ /* The Bus Pirate can't handle UART input buffer overflow in BBIO mode, and sending a sequence
+ * of 0x00 too fast apparently triggers such an UART input buffer overflow.
+ */
+ internal_sleep(10000);
+ }
+ /* We know that 20 commands of \0 should elicit at least one BBIO1 response. */
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO")))
+ return ret;
+
+ /* Reset the Bus Pirate. */
+ bp_commbuf[0] = 0x0f;
+ /* Send the command, don't read the response. */
+ if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0)))
+ return ret;
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "irate ")))
+ return ret;
+ /* Read the hardware version string. Last byte of the buffer is reserved for \0. */
+ for (i = 0; i < DEFAULT_BUFSIZE - 1; i++) {
+ if ((ret = buspirate_sendrecv(bp_commbuf + i, 0, 1)))
+ return ret;
+ if (strchr("\r\n\t ", bp_commbuf[i]))
+ break;
+ }
+ bp_commbuf[i] = '\0';
+ msg_pdbg("Detected Bus Pirate hardware %s\n", bp_commbuf);
+
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "irmware ")))
+ return ret;
+ /* Read the firmware version string. Last byte of the buffer is reserved for \0. */
+ for (i = 0; i < DEFAULT_BUFSIZE - 1; i++) {
+ if ((ret = buspirate_sendrecv(bp_commbuf + i, 0, 1)))
+ return ret;
+ if (strchr("\r\n\t ", bp_commbuf[i]))
+ break;
+ }
+ bp_commbuf[i] = '\0';
+ msg_pdbg("Detected Bus Pirate firmware ");
+ if (bp_commbuf[0] != 'v')
+ msg_pdbg("(unknown version number format)");
+ else if (!strchr("0123456789", bp_commbuf[1]))
+ msg_pdbg("(unknown version number format)");
+ else {
+ fw_version_major = strtoul((char *)bp_commbuf + 1, &tmp, 10);
+ while ((*tmp != '\0') && !strchr("0123456789", *tmp))
+ tmp++;
+ fw_version_minor = strtoul(tmp, NULL, 10);
+ msg_pdbg("%u.%u", fw_version_major, fw_version_minor);
+ }
+ msg_pdbg2(" (\"%s\")", bp_commbuf);
+ msg_pdbg("\n");
+
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "HiZ>")))
+ return ret;
+
+ /* Tell the user about missing SPI binary mode in firmware 2.3 and older. */
+ if (BP_FWVERSION(fw_version_major, fw_version_minor) < BP_FWVERSION(2, 4)) {
+ msg_pinfo("Bus Pirate firmware 2.3 and older does not support binary SPI access.\n");
+ msg_pinfo("Please upgrade to the latest firmware (at least 2.4).\n");
+ return SPI_PROGRAMMER_ERROR;
+ }
+
+ /* Use fast SPI mode in firmware 5.5 and newer. */
+ if (BP_FWVERSION(fw_version_major, fw_version_minor) >= BP_FWVERSION(5, 5)) {
+ msg_pdbg("Using SPI command set v2.\n");
+ /* Sensible default buffer size. */
+ if (buspirate_commbuf_grow(260 + 5))
+ return ERROR_OOM;
+ spi_master_buspirate.max_data_read = 2048;
+ spi_master_buspirate.max_data_write = 256;
+ spi_master_buspirate.command = buspirate_spi_send_command_v2;
+ } else {
+ msg_pinfo("Bus Pirate firmware 5.4 and older does not support fast SPI access.\n");
+ msg_pinfo("Reading/writing a flash chip may take hours.\n");
+ msg_pinfo("It is recommended to upgrade to firmware 5.5 or newer.\n");
+ /* Sensible default buffer size. */
+ if (buspirate_commbuf_grow(16 + 3))
+ return ERROR_OOM;
+ spi_master_buspirate.max_data_read = 12;
+ spi_master_buspirate.max_data_write = 12;
+ spi_master_buspirate.command = buspirate_spi_send_command_v1;
+ }
+
+ /* Workaround for broken speed settings in firmware 6.1 and older. */
+ if (BP_FWVERSION(fw_version_major, fw_version_minor) < BP_FWVERSION(6, 2))
+ if (spispeed > 0x4) {
+ msg_perr("Bus Pirate firmware 6.1 and older does not support SPI speeds above 2 MHz. "
+ "Limiting speed to 2 MHz.\n");
+ msg_pinfo("It is recommended to upgrade to firmware 6.2 or newer.\n");
+ spispeed = 0x4;
+ }
+
+ /* This works because speeds numbering starts at 0 and is contiguous. */
+ msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed].name);
+
+ /* Enter raw bitbang mode */
+ for (i = 0; i < 20; i++) {
+ bp_commbuf[0] = 0x00;
+ if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0)))
+ return ret;
+ }
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO")))
+ return ret;
+ if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1)))
+ return ret;
+ msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[0]);
+ if (bp_commbuf[0] != '1') {
+ msg_perr("Can't handle raw bitbang mode version %c!\n", bp_commbuf[0]);
+ return 1;
+ }
+ /* Enter raw SPI mode */
+ bp_commbuf[0] = 0x01;
+ ret = buspirate_sendrecv(bp_commbuf, 1, 0);
+ if ((ret = buspirate_wait_for_string(bp_commbuf, "SPI")))
+ return ret;
+ if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1)))
+ return ret;
+ msg_pdbg("Raw SPI mode version %c\n", bp_commbuf[0]);
+ if (bp_commbuf[0] != '1') {
+ msg_perr("Can't handle raw SPI mode version %c!\n", bp_commbuf[0]);
+ return 1;
+ }
+
+ /* Initial setup (SPI peripherals config): Enable power, CS high, AUX */
+ bp_commbuf[0] = 0x40 | 0x0b;
+ if (pullup == 1) {
+ bp_commbuf[0] |= (1 << 2);
+ msg_pdbg("Enabling pull-up resistors.\n");
+ }
+ ret = buspirate_sendrecv(bp_commbuf, 1, 1);
+ if (ret)
+ return 1;
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while setting power/CS/AUX(/Pull-up resistors)!\n");
+ return 1;
+ }
+
+ /* Set SPI speed */
+ bp_commbuf[0] = 0x60 | spispeed;
+ ret = buspirate_sendrecv(bp_commbuf, 1, 1);
+ if (ret)
+ return 1;
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while setting SPI speed!\n");
+ return 1;
+ }
+
+ /* Set SPI config: output type, idle, clock edge, sample */
+ bp_commbuf[0] = 0x80 | 0xa;
+ ret = buspirate_sendrecv(bp_commbuf, 1, 1);
+ if (ret)
+ return 1;
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while setting SPI config!\n");
+ return 1;
+ }
+
+ /* De-assert CS# */
+ bp_commbuf[0] = 0x03;
+ ret = buspirate_sendrecv(bp_commbuf, 1, 1);
+ if (ret)
+ return 1;
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while raising CS#!\n");
+ return 1;
+ }
+
+ register_spi_master(&spi_master_buspirate);
+
+ return 0;
+}
+
+static int buspirate_spi_send_command_v1(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr)
+{
+ unsigned int i = 0;
+ int ret = 0;
+
+ if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16)
+ return SPI_INVALID_LENGTH;
+
+ /* 3 bytes extra for CS#, len, CS#. */
+ if (buspirate_commbuf_grow(writecnt + readcnt + 3))
+ return ERROR_OOM;
+
+ /* Assert CS# */
+ bp_commbuf[i++] = 0x02;
+
+ bp_commbuf[i++] = 0x10 | (writecnt + readcnt - 1);
+ memcpy(bp_commbuf + i, writearr, writecnt);
+ i += writecnt;
+ memset(bp_commbuf + i, 0, readcnt);
+
+ i += readcnt;
+ /* De-assert CS# */
+ bp_commbuf[i++] = 0x03;
+
+ ret = buspirate_sendrecv(bp_commbuf, i, i);
+
+ if (ret) {
+ msg_perr("Bus Pirate communication error!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while lowering CS#!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ if (bp_commbuf[1] != 0x01) {
+ msg_perr("Protocol error while reading/writing SPI!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ if (bp_commbuf[i - 1] != 0x01) {
+ msg_perr("Protocol error while raising CS#!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ /* Skip CS#, length, writearr. */
+ memcpy(readarr, bp_commbuf + 2 + writecnt, readcnt);
+
+ return ret;
+}
+
+static int buspirate_spi_send_command_v2(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr)
+{
+ int i = 0, ret = 0;
+
+ if (writecnt > 4096 || readcnt > 4096 || (readcnt + writecnt) > 4096)
+ return SPI_INVALID_LENGTH;
+
+ /* 5 bytes extra for command, writelen, readlen.
+ * 1 byte extra for Ack/Nack.
+ */
+ if (buspirate_commbuf_grow(max(writecnt + 5, readcnt + 1)))
+ return ERROR_OOM;
+
+ /* Combined SPI write/read. */
+ bp_commbuf[i++] = 0x04;
+ bp_commbuf[i++] = (writecnt >> 8) & 0xff;
+ bp_commbuf[i++] = writecnt & 0xff;
+ bp_commbuf[i++] = (readcnt >> 8) & 0xff;
+ bp_commbuf[i++] = readcnt & 0xff;
+ memcpy(bp_commbuf + i, writearr, writecnt);
+
+ ret = buspirate_sendrecv(bp_commbuf, i + writecnt, 1 + readcnt);
+
+ if (ret) {
+ msg_perr("Bus Pirate communication error!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ if (bp_commbuf[0] != 0x01) {
+ msg_perr("Protocol error while sending SPI write/read!\n");
+ return SPI_GENERIC_ERROR;
+ }
+
+ /* Skip Ack. */
+ memcpy(readarr, bp_commbuf + 1, readcnt);
+
+ return ret;
+}
diff --git a/cbtable.c b/cbtable.c
new file mode 100644
index 0000000..1a74e46
--- /dev/null
+++ b/cbtable.c
@@ -0,0 +1,304 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2002 Steven James <pyro@linuxlabs.com>
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
+ * Copyright (C) 2006-2009 coresystems GmbH
+ * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH)
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <strings.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "coreboot_tables.h"
+
+static char *cb_vendor = NULL, *cb_model = NULL;
+
+/* Tries to find coreboot IDs in the supplied image and compares them to the current IDs.
+ * Returns...
+ * -1 if IDs in the image do not match the IDs embedded in the current firmware,
+ * 0 if the IDs could not be found in the image or if they match correctly.
+ */
+int cb_check_image(uint8_t *image, int size)
+{
+ unsigned int *walk;
+ unsigned int mb_part_offset, mb_vendor_offset;
+ char *mb_part, *mb_vendor;
+
+ walk = (unsigned int *)(image + size - 0x10);
+ walk--;
+
+ if ((*walk) == 0 || ((*walk) & 0x3ff) != 0) {
+ /* Some NVIDIA chipsets store chipset soft straps (IIRC Hypertransport init info etc.) in
+ * flash at exactly the location where coreboot image size, coreboot vendor name pointer and
+ * coreboot board name pointer are usually stored. In this case coreboot uses an alternate
+ * location for the coreboot image data. */
+ walk = (unsigned int *)(image + size - 0x80);
+ walk--;
+ }
+
+ /*
+ * Check if coreboot last image size is 0 or not a multiple of 1k or
+ * bigger than the chip or if the pointers to vendor ID or mainboard ID
+ * are outside the image of if the start of ID strings are nonsensical
+ * (nonprintable and not \0).
+ */
+ mb_part_offset = *(walk - 1);
+ mb_vendor_offset = *(walk - 2);
+ if ((*walk) == 0 || ((*walk) & 0x3ff) != 0 || (*walk) > size ||
+ mb_part_offset > size || mb_vendor_offset > size) {
+ msg_pdbg("Flash image seems to be a legacy BIOS. Disabling coreboot-related checks.\n");
+ return 0;
+ }
+
+ mb_part = (char *)(image + size - mb_part_offset);
+ mb_vendor = (char *)(image + size - mb_vendor_offset);
+ if (!isprint((unsigned char)*mb_part) ||
+ !isprint((unsigned char)*mb_vendor)) {
+ msg_pdbg("Flash image seems to have garbage in the ID location. "
+ "Disabling coreboot-related checks.\n");
+ return 0;
+ }
+
+ msg_pdbg("coreboot last image size (not ROM size) is %d bytes.\n", *walk);
+
+ msg_pdbg("Manufacturer: %s\n", mb_vendor);
+ msg_pdbg("Mainboard ID: %s\n", mb_part);
+
+ /* If these are not set, the coreboot table was not found. */
+ if (!cb_vendor || !cb_model)
+ return 0;
+
+ /* These comparisons are case insensitive to make things a little less user^Werror prone. */
+ if (!strcasecmp(mb_vendor, cb_vendor) && !strcasecmp(mb_part, cb_model)) {
+ msg_pdbg2("This coreboot image matches this mainboard.\n");
+ } else {
+ msg_perr("This coreboot image (%s:%s) does not appear to\n"
+ "be correct for the detected mainboard (%s:%s).\n",
+ mb_vendor, mb_part, cb_vendor, cb_model);
+ return -1;
+ }
+
+ return 0;
+}
+
+static unsigned long compute_checksum(void *addr, unsigned long length)
+{
+ uint8_t *ptr;
+ volatile union {
+ uint8_t byte[2];
+ uint16_t word;
+ } chksum;
+ unsigned long sum;
+ unsigned long i;
+
+ /* In the most straight forward way possible,
+ * compute an ip style checksum.
+ */
+ sum = 0;
+ ptr = addr;
+ for (i = 0; i < length; i++) {
+ unsigned long value;
+ value = ptr[i];
+ if (i & 1) {
+ value <<= 8;
+ }
+ /* Add the new value */
+ sum += value;
+ /* Wrap around the carry */
+ if (sum > 0xFFFF) {
+ sum = (sum + (sum >> 16)) & 0xFFFF;
+ }
+ }
+ chksum.byte[0] = sum & 0xff;
+ chksum.byte[1] = (sum >> 8) & 0xff;
+
+ return (~chksum.word) & 0xFFFF;
+}
+
+#define for_each_lbrec(head, rec) \
+ for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
+ (((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes)) && \
+ (rec->size >= 1) && \
+ ((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
+ rec = (struct lb_record *)(((char *)rec) + rec->size))
+
+static int count_lb_records(struct lb_header *head)
+{
+ struct lb_record *rec;
+ int count;
+
+ count = 0;
+ for_each_lbrec(head, rec) {
+ count++;
+ }
+
+ return count;
+}
+
+static struct lb_header *find_lb_table(void *base, unsigned long start,
+ unsigned long end)
+{
+ unsigned long addr;
+
+ /* For now be stupid.... */
+ for (addr = start; addr < end; addr += 16) {
+ struct lb_header *head =
+ (struct lb_header *)(((char *)base) + addr);
+ struct lb_record *recs =
+ (struct lb_record *)(((char *)base) + addr + sizeof(*head));
+ if (memcmp(head->signature, "LBIO", 4) != 0)
+ continue;
+ msg_pdbg("Found candidate at: %08lx-%08lx\n",
+ addr, addr + head->table_bytes);
+ if (head->header_bytes != sizeof(*head)) {
+ msg_perr("Header bytes of %d are incorrect.\n",
+ head->header_bytes);
+ continue;
+ }
+ if (count_lb_records(head) != head->table_entries) {
+ msg_perr("Bad record count: %d.\n",
+ head->table_entries);
+ continue;
+ }
+ if (compute_checksum((uint8_t *) head, sizeof(*head)) != 0) {
+ msg_perr("Bad header checksum.\n");
+ continue;
+ }
+ if (compute_checksum(recs, head->table_bytes)
+ != head->table_checksum) {
+ msg_perr("Bad table checksum: %04x.\n",
+ head->table_checksum);
+ continue;
+ }
+ msg_pdbg("Found coreboot table at 0x%08lx.\n", addr);
+ return head;
+
+ };
+
+ return NULL;
+}
+
+static void find_mainboard(struct lb_record *ptr, unsigned long addr)
+{
+ struct lb_mainboard *rec;
+ int max_size;
+ char vendor[256], part[256];
+
+ rec = (struct lb_mainboard *)ptr;
+ max_size = rec->size - sizeof(*rec);
+ msg_pdbg("Vendor ID: %.*s, part ID: %.*s\n",
+ max_size - rec->vendor_idx,
+ rec->strings + rec->vendor_idx,
+ max_size - rec->part_number_idx,
+ rec->strings + rec->part_number_idx);
+ snprintf(vendor, 255, "%.*s", max_size - rec->vendor_idx, rec->strings + rec->vendor_idx);
+ snprintf(part, 255, "%.*s", max_size - rec->part_number_idx, rec->strings + rec->part_number_idx);
+
+ cb_vendor = strdup(vendor);
+ cb_model = strdup(part);
+}
+
+static struct lb_record *next_record(struct lb_record *rec)
+{
+ return (struct lb_record *)(((char *)rec) + rec->size);
+}
+
+static void search_lb_records(struct lb_record *rec, struct lb_record *last, unsigned long addr)
+{
+ struct lb_record *next;
+ int count;
+ count = 0;
+
+ for (next = next_record(rec); (rec < last) && (next <= last);
+ rec = next, addr += rec->size) {
+ next = next_record(rec);
+ count++;
+ if (rec->tag == LB_TAG_MAINBOARD) {
+ find_mainboard(rec, addr);
+ break;
+ }
+ }
+}
+
+#define BYTES_TO_MAP (1024*1024)
+/* returns 0 if the table was parsed successfully and cb_vendor/cb_model have been set. */
+int cb_parse_table(const char **vendor, const char **model)
+{
+ uint8_t *table_area;
+ unsigned long addr, start;
+ struct lb_header *lb_table;
+ struct lb_record *rec, *last;
+
+#if defined(__MACH__) && defined(__APPLE__)
+ /* This is a hack. DirectHW fails to map physical address 0x00000000.
+ * Why?
+ */
+ start = 0x400;
+#else
+ start = 0x0;
+#endif
+ table_area = physmap_ro_unaligned("low megabyte", start, BYTES_TO_MAP - start);
+ if (ERROR_PTR == table_area) {
+ msg_perr("Failed getting access to coreboot low tables.\n");
+ return -1;
+ }
+
+ lb_table = find_lb_table(table_area, 0x00000, 0x1000);
+ if (!lb_table)
+ lb_table = find_lb_table(table_area, 0xf0000 - start, BYTES_TO_MAP - start);
+ if (lb_table) {
+ struct lb_forward *forward = (struct lb_forward *)
+ (((char *)lb_table) + lb_table->header_bytes);
+ if (forward->tag == LB_TAG_FORWARD) {
+ start = forward->forward;
+ start &= ~(getpagesize() - 1);
+ physunmap_unaligned(table_area, BYTES_TO_MAP);
+ // FIXME: table_area is never unmapped below, nor is it unmapped above in the no-forward case
+ table_area = physmap_ro_unaligned("high tables", start, BYTES_TO_MAP);
+ if (ERROR_PTR == table_area) {
+ msg_perr("Failed getting access to coreboot high tables.\n");
+ return -1;
+ }
+ lb_table = find_lb_table(table_area, 0x00000, 0x1000);
+ }
+ }
+
+ if (!lb_table) {
+ msg_pdbg("No coreboot table found.\n");
+ return -1;
+ }
+
+ addr = ((char *)lb_table) - ((char *)table_area) + start;
+ msg_pinfo("coreboot table found at 0x%lx.\n",
+ (unsigned long)lb_table - (unsigned long)table_area + start);
+ rec = (struct lb_record *)(((char *)lb_table) + lb_table->header_bytes);
+ last = (struct lb_record *)(((char *)rec) + lb_table->table_bytes);
+ msg_pdbg("coreboot header(%d) checksum: %04x table(%d) checksum: %04x entries: %d\n",
+ lb_table->header_bytes, lb_table->header_checksum,
+ lb_table->table_bytes, lb_table->table_checksum,
+ lb_table->table_entries);
+ search_lb_records(rec, last, addr + lb_table->header_bytes);
+ *vendor = cb_vendor;
+ *model = cb_model;
+ return 0;
+}
diff --git a/ch341a_spi.c b/ch341a_spi.c
new file mode 100644
index 0000000..6eb2804
--- /dev/null
+++ b/ch341a_spi.c
@@ -0,0 +1,535 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 asbokid <ballymunboy@gmail.com>
+ * Copyright (C) 2014 Pluto Yang <yangyj.ee@gmail.com>
+ * Copyright (C) 2015-2016 Stefan Tauner
+ * Copyright (C) 2015 Urja Rannikko <urjaman@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <libusb.h>
+#include "flash.h"
+#include "programmer.h"
+
+/* LIBUSB_CALL ensures the right calling conventions on libusb callbacks.
+ * However, the macro is not defined everywhere. m(
+ */
+#ifndef LIBUSB_CALL
+#define LIBUSB_CALL
+#endif
+
+#define USB_TIMEOUT 1000 /* 1000 ms is plenty and we have no backup strategy anyway. */
+#define WRITE_EP 0x02
+#define READ_EP 0x82
+
+#define CH341_PACKET_LENGTH 0x20
+#define CH341_MAX_PACKETS 256
+#define CH341_MAX_PACKET_LEN (CH341_PACKET_LENGTH * CH341_MAX_PACKETS)
+
+#define CH341A_CMD_SET_OUTPUT 0xA1
+#define CH341A_CMD_IO_ADDR 0xA2
+#define CH341A_CMD_PRINT_OUT 0xA3
+#define CH341A_CMD_SPI_STREAM 0xA8
+#define CH341A_CMD_SIO_STREAM 0xA9
+#define CH341A_CMD_I2C_STREAM 0xAA
+#define CH341A_CMD_UIO_STREAM 0xAB
+
+#define CH341A_CMD_I2C_STM_START 0x74
+#define CH341A_CMD_I2C_STM_STOP 0x75
+#define CH341A_CMD_I2C_STM_OUT 0x80
+#define CH341A_CMD_I2C_STM_IN 0xC0
+#define CH341A_CMD_I2C_STM_MAX ( min( 0x3F, CH341_PACKET_LENGTH ) )
+#define CH341A_CMD_I2C_STM_SET 0x60 // bit 2: SPI with two data pairs D5,D4=out, D7,D6=in
+#define CH341A_CMD_I2C_STM_US 0x40
+#define CH341A_CMD_I2C_STM_MS 0x50
+#define CH341A_CMD_I2C_STM_DLY 0x0F
+#define CH341A_CMD_I2C_STM_END 0x00
+
+#define CH341A_CMD_UIO_STM_IN 0x00
+#define CH341A_CMD_UIO_STM_DIR 0x40
+#define CH341A_CMD_UIO_STM_OUT 0x80
+#define CH341A_CMD_UIO_STM_US 0xC0
+#define CH341A_CMD_UIO_STM_END 0x20
+
+#define CH341A_STM_I2C_20K 0x00
+#define CH341A_STM_I2C_100K 0x01
+#define CH341A_STM_I2C_400K 0x02
+#define CH341A_STM_I2C_750K 0x03
+#define CH341A_STM_SPI_DBL 0x04
+
+
+/* Number of parallel IN transfers. 32 seems to produce the most stable throughput on Windows. */
+#define USB_IN_TRANSFERS 32
+
+/* We need to use many queued IN transfers for any resemblance of performance (especially on Windows)
+ * because USB spec says that transfers end on non-full packets and the device sends the 31 reply
+ * data bytes to each 32-byte packet with command + 31 bytes of data... */
+static struct libusb_transfer *transfer_out = NULL;
+static struct libusb_transfer *transfer_ins[USB_IN_TRANSFERS] = {0};
+
+/* Accumulate delays to be plucked between CS deassertion and CS assertions. */
+static unsigned int stored_delay_us = 0;
+
+static struct libusb_device_handle *handle = NULL;
+
+const struct dev_entry devs_ch341a_spi[] = {
+ {0x1A86, 0x5512, OK, "Winchiphead (WCH)", "CH341A"},
+
+ {0},
+};
+
+enum trans_state {TRANS_ACTIVE = -2, TRANS_ERR = -1, TRANS_IDLE = 0};
+
+static void print_hex(const void *buf, size_t len)
+{
+ size_t i;
+ for (i = 0; i < len; i++) {
+ msg_pspew(" %02x", ((uint8_t *)buf)[i]);
+ if (i % CH341_PACKET_LENGTH == CH341_PACKET_LENGTH - 1)
+ msg_pspew("\n");
+ }
+}
+
+static void cb_common(const char *func, struct libusb_transfer *transfer)
+{
+ int *transfer_cnt = (int*)transfer->user_data;
+
+ if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+ /* Silently ACK and exit. */
+ *transfer_cnt = TRANS_IDLE;
+ return;
+ }
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ msg_perr("\n%s: error: %s\n", func, libusb_error_name(transfer->status));
+ *transfer_cnt = TRANS_ERR;
+ } else {
+ *transfer_cnt = transfer->actual_length;
+ }
+}
+
+/* callback for bulk out async transfer */
+static void LIBUSB_CALL cb_out(struct libusb_transfer *transfer)
+{
+ cb_common(__func__, transfer);
+}
+
+/* callback for bulk in async transfer */
+static void LIBUSB_CALL cb_in(struct libusb_transfer *transfer)
+{
+ cb_common(__func__, transfer);
+}
+
+static int32_t usb_transfer(const char *func, unsigned int writecnt, unsigned int readcnt, const uint8_t *writearr, uint8_t *readarr)
+{
+ if (handle == NULL)
+ return -1;
+
+ int state_out = TRANS_IDLE;
+ transfer_out->buffer = (uint8_t*)writearr;
+ transfer_out->length = writecnt;
+ transfer_out->user_data = &state_out;
+
+ /* Schedule write first */
+ if (writecnt > 0) {
+ state_out = TRANS_ACTIVE;
+ int ret = libusb_submit_transfer(transfer_out);
+ if (ret) {
+ msg_perr("%s: failed to submit OUT transfer: %s\n", func, libusb_error_name(ret));
+ state_out = TRANS_ERR;
+ goto err;
+ }
+ }
+
+ /* Handle all asynchronous packets as long as we have stuff to write or read. The write(s) simply need
+ * to complete but we need to scheduling reads as long as we are not done. */
+ unsigned int free_idx = 0; /* The IN transfer we expect to be free next. */
+ unsigned int in_idx = 0; /* The IN transfer we expect to be completed next. */
+ unsigned int in_done = 0;
+ unsigned int in_active = 0;
+ unsigned int out_done = 0;
+ uint8_t *in_buf = readarr;
+ int state_in[USB_IN_TRANSFERS] = {0};
+ do {
+ /* Schedule new reads as long as there are free transfers and unscheduled bytes to read. */
+ while ((in_done + in_active) < readcnt && state_in[free_idx] == TRANS_IDLE) {
+ unsigned int cur_todo = min(CH341_PACKET_LENGTH - 1, readcnt - in_done - in_active);
+ transfer_ins[free_idx]->length = cur_todo;
+ transfer_ins[free_idx]->buffer = in_buf;
+ transfer_ins[free_idx]->user_data = &state_in[free_idx];
+ int ret = libusb_submit_transfer(transfer_ins[free_idx]);
+ if (ret) {
+ state_in[free_idx] = TRANS_ERR;
+ msg_perr("%s: failed to submit IN transfer: %s\n",
+ func, libusb_error_name(ret));
+ goto err;
+ }
+ in_buf += cur_todo;
+ in_active += cur_todo;
+ state_in[free_idx] = TRANS_ACTIVE;
+ free_idx = (free_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */
+ }
+
+ /* Actually get some work done. */
+ libusb_handle_events_timeout(NULL, &(struct timeval){1, 0});
+
+ /* Check for the write */
+ if (out_done < writecnt) {
+ if (state_out == TRANS_ERR) {
+ goto err;
+ } else if (state_out > 0) {
+ out_done += state_out;
+ state_out = TRANS_IDLE;
+ }
+ }
+ /* Check for completed transfers. */
+ while (state_in[in_idx] != TRANS_IDLE && state_in[in_idx] != TRANS_ACTIVE) {
+ if (state_in[in_idx] == TRANS_ERR) {
+ goto err;
+ }
+ /* If a transfer is done, record the number of bytes read and reuse it later. */
+ in_done += state_in[in_idx];
+ in_active -= state_in[in_idx];
+ state_in[in_idx] = TRANS_IDLE;
+ in_idx = (in_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */
+ }
+ } while ((out_done < writecnt) || (in_done < readcnt));
+
+ if (out_done > 0) {
+ msg_pspew("Wrote %d bytes:\n", out_done);
+ print_hex(writearr, out_done);
+ msg_pspew("\n\n");
+ }
+ if (in_done > 0) {
+ msg_pspew("Read %d bytes:\n", in_done);
+ print_hex(readarr, in_done);
+ msg_pspew("\n\n");
+ }
+ return 0;
+err:
+ /* Clean up on errors. */
+ msg_perr("%s: Failed to %s %d bytes\n", func, (state_out == TRANS_ERR) ? "write" : "read",
+ (state_out == TRANS_ERR) ? writecnt : readcnt);
+ /* First, we must cancel any ongoing requests and wait for them to be canceled. */
+ if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) {
+ if (libusb_cancel_transfer(transfer_out) != 0)
+ state_out = TRANS_ERR;
+ }
+ if (readcnt > 0) {
+ unsigned int i;
+ for (i = 0; i < USB_IN_TRANSFERS; i++) {
+ if (state_in[i] == TRANS_ACTIVE)
+ if (libusb_cancel_transfer(transfer_ins[i]) != 0)
+ state_in[i] = TRANS_ERR;
+ }
+ }
+
+ /* Wait for cancellations to complete. */
+ while (1) {
+ bool finished = true;
+ if ((writecnt > 0) && (state_out == TRANS_ACTIVE))
+ finished = false;
+ if (readcnt > 0) {
+ unsigned int i;
+ for (i = 0; i < USB_IN_TRANSFERS; i++) {
+ if (state_in[i] == TRANS_ACTIVE)
+ finished = false;
+ }
+ }
+ if (finished)
+ break;
+ libusb_handle_events_timeout(NULL, &(struct timeval){1, 0});
+ }
+ return -1;
+}
+
+/* Set the I2C bus speed (speed(b1b0): 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz).
+ * Set the SPI bus data width (speed(b2): 0 = Single, 1 = Double). */
+static int32_t config_stream(uint32_t speed)
+{
+ if (handle == NULL)
+ return -1;
+
+ uint8_t buf[] = {
+ CH341A_CMD_I2C_STREAM,
+ CH341A_CMD_I2C_STM_SET | (speed & 0x7),
+ CH341A_CMD_I2C_STM_END
+ };
+
+ int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL);
+ if (ret < 0) {
+ msg_perr("Could not configure stream interface.\n");
+ }
+ return ret;
+}
+
+/* ch341 requires LSB first, swap the bit order before send and after receive */
+static uint8_t swap_byte(uint8_t x)
+{
+ x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa);
+ x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc);
+ x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0);
+ return x;
+}
+
+/* The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip:
+ * UIO CH341A SPI CH341A SPI name
+ * 0 D0/15 CS/1 (CS0)
+ * 1 D1/16 unused (CS1)
+ * 2 D2/17 unused (CS2)
+ * 3 D3/18 SCK/6 (DCK)
+ * 4 D4/19 unused (DOUT2)
+ * 5 D5/20 SI/5 (DOUT)
+ * - The UIO stream commands seem to only have 6 bits of output, and D6/D7 are the SPI inputs,
+ * mapped as follows:
+ * D6/21 unused (DIN2)
+ * D7/22 SO/2 (DIN)
+ */
+static int32_t enable_pins(bool enable)
+{
+ uint8_t buf[] = {
+ CH341A_CMD_UIO_STREAM,
+ CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1
+ CH341A_CMD_UIO_STM_DIR | (enable ? 0x3F : 0x00), // Interface output enable / disable
+ CH341A_CMD_UIO_STM_END,
+ };
+
+ int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL);
+ if (ret < 0) {
+ msg_perr("Could not %sable output pins.\n", enable ? "en" : "dis");
+ }
+ return ret;
+}
+
+/* De-assert and assert CS in one operation. */
+static void pluck_cs(uint8_t *ptr)
+{
+ /* This was measured to give a minumum deassertion time of 2.25 us,
+ * >20x more than needed for most SPI chips (100ns). */
+ int delay_cnt = 2;
+ if (stored_delay_us) {
+ delay_cnt = (stored_delay_us * 4) / 3;
+ stored_delay_us = 0;
+ }
+ *ptr++ = CH341A_CMD_UIO_STREAM;
+ *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* deasserted */
+ int i;
+ for (i = 0; i < delay_cnt; i++)
+ *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x37; /* "delay" */
+ *ptr++ = CH341A_CMD_UIO_STM_OUT | 0x36; /* asserted */
+ *ptr++ = CH341A_CMD_UIO_STM_END;
+}
+
+void ch341a_spi_delay(unsigned int usecs)
+{
+ /* There is space for 28 bytes instructions of 750 ns each in the CS packet (32 - 4 for the actual CS
+ * instructions), thus max 21 us, but we avoid getting too near to this boundary and use
+ * internal_delay() for durations over 20 us. */
+ if ((usecs + stored_delay_us) > 20) {
+ unsigned int inc = 20 - stored_delay_us;
+ internal_delay(usecs - inc);
+ usecs = inc;
+ }
+ stored_delay_us += usecs;
+}
+
+static int ch341a_spi_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr)
+{
+ if (handle == NULL)
+ return -1;
+
+ /* How many packets ... */
+ const size_t packets = (writecnt + readcnt + CH341_PACKET_LENGTH - 2) / (CH341_PACKET_LENGTH - 1);
+
+ /* We pluck CS/timeout handling into the first packet thus we need to allocate one extra package. */
+ uint8_t wbuf[packets+1][CH341_PACKET_LENGTH];
+ uint8_t rbuf[writecnt + readcnt];
+ /* Initialize the write buffer to zero to prevent writing random stack contents to device. */
+ memset(wbuf[0], 0, CH341_PACKET_LENGTH);
+
+ uint8_t *ptr = wbuf[0];
+ /* CS usage is optimized by doing both transitions in one packet.
+ * Final transition to deselected state is in the pin disable. */
+ pluck_cs(ptr);
+ unsigned int write_left = writecnt;
+ unsigned int read_left = readcnt;
+ unsigned int p;
+ for (p = 0; p < packets; p++) {
+ unsigned int write_now = min(CH341_PACKET_LENGTH - 1, write_left);
+ unsigned int read_now = min ((CH341_PACKET_LENGTH - 1) - write_now, read_left);
+ ptr = wbuf[p+1];
+ *ptr++ = CH341A_CMD_SPI_STREAM;
+ unsigned int i;
+ for (i = 0; i < write_now; ++i)
+ *ptr++ = swap_byte(*writearr++);
+ if (read_now) {
+ memset(ptr, 0xFF, read_now);
+ read_left -= read_now;
+ }
+ write_left -= write_now;
+ }
+
+ int32_t ret = usb_transfer(__func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt,
+ writecnt + readcnt, wbuf[0], rbuf);
+ if (ret < 0)
+ return -1;
+
+ unsigned int i;
+ for (i = 0; i < readcnt; i++) {
+ *readarr++ = swap_byte(rbuf[writecnt + i]);
+ }
+
+ return 0;
+}
+
+static const struct spi_master spi_master_ch341a_spi = {
+ .type = SPI_CONTROLLER_CH341A_SPI,
+ /* flashrom's current maximum is 256 B. CH341A was tested on Linux and Windows to accept atleast
+ * 128 kB. Basically there should be no hard limit because transfers are broken up into USB packets
+ * sent to the device and most of their payload streamed via SPI. */
+ .max_data_read = 4 * 1024,
+ .max_data_write = 4 * 1024,
+ .command = ch341a_spi_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static int ch341a_spi_shutdown(void *data)
+{
+ if (handle == NULL)
+ return -1;
+
+ enable_pins(false);
+ libusb_free_transfer(transfer_out);
+ transfer_out = NULL;
+ int i;
+ for (i = 0; i < USB_IN_TRANSFERS; i++) {
+ libusb_free_transfer(transfer_ins[i]);
+ transfer_ins[i] = NULL;
+ }
+ libusb_release_interface(handle, 0);
+ libusb_close(handle);
+ libusb_exit(NULL);
+ handle = NULL;
+ return 0;
+}
+
+int ch341a_spi_init(void)
+{
+ if (handle != NULL) {
+ msg_cerr("%s: handle already set! Please report a bug at flashrom@flashrom.org\n", __func__);
+ return -1;
+ }
+
+ int32_t ret = libusb_init(NULL);
+ if (ret < 0) {
+ msg_perr("Couldnt initialize libusb!\n");
+ return -1;
+ }
+
+ libusb_set_debug(NULL, 3); // Enable information, warning and error messages (only).
+
+ uint16_t vid = devs_ch341a_spi[0].vendor_id;
+ uint16_t pid = devs_ch341a_spi[0].device_id;
+ handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
+ if (handle == NULL) {
+ msg_perr("Couldn't open device %04x:%04x.\n", vid, pid);
+ return -1;
+ }
+
+/* libusb_detach_kernel_driver() and friends basically only work on Linux. We simply try to detach on Linux
+ * without a lot of passion here. If that works fine else we will fail on claiming the interface anyway. */
+#if IS_LINUX
+ ret = libusb_detach_kernel_driver(handle, 0);
+ if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
+ msg_pwarn("Detaching kernel drivers is not supported. Further accesses may fail.\n");
+ } else if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) {
+ msg_pwarn("Failed to detach kernel driver: '%s'. Further accesses will probably fail.\n",
+ libusb_error_name(ret));
+ }
+#endif
+
+ ret = libusb_claim_interface(handle, 0);
+ if (ret != 0) {
+ msg_perr("Failed to claim interface 0: '%s'\n", libusb_error_name(ret));
+ goto close_handle;
+ }
+
+ struct libusb_device *dev;
+ if (!(dev = libusb_get_device(handle))) {
+ msg_perr("Failed to get device from device handle.\n");
+ goto close_handle;
+ }
+
+ struct libusb_device_descriptor desc;
+ ret = libusb_get_device_descriptor(dev, &desc);
+ if (ret < 0) {
+ msg_perr("Failed to get device descriptor: '%s'\n", libusb_error_name(ret));
+ goto release_interface;
+ }
+
+ msg_pdbg("Device revision is %d.%01d.%01d\n",
+ (desc.bcdDevice >> 8) & 0x00FF,
+ (desc.bcdDevice >> 4) & 0x000F,
+ (desc.bcdDevice >> 0) & 0x000F);
+
+ /* Allocate and pre-fill transfer structures. */
+ transfer_out = libusb_alloc_transfer(0);
+ if (!transfer_out) {
+ msg_perr("Failed to alloc libusb OUT transfer\n");
+ goto release_interface;
+ }
+ int i;
+ for (i = 0; i < USB_IN_TRANSFERS; i++) {
+ transfer_ins[i] = libusb_alloc_transfer(0);
+ if (transfer_ins[i] == NULL) {
+ msg_perr("Failed to alloc libusb IN transfer %d\n", i);
+ goto dealloc_transfers;
+ }
+ }
+ /* We use these helpers but dont fill the actual buffer yet. */
+ libusb_fill_bulk_transfer(transfer_out, handle, WRITE_EP, NULL, 0, cb_out, NULL, USB_TIMEOUT);
+ for (i = 0; i < USB_IN_TRANSFERS; i++)
+ libusb_fill_bulk_transfer(transfer_ins[i], handle, READ_EP, NULL, 0, cb_in, NULL, USB_TIMEOUT);
+
+ if ((config_stream(CH341A_STM_I2C_100K) < 0) || (enable_pins(true) < 0))
+ goto dealloc_transfers;
+
+ register_shutdown(ch341a_spi_shutdown, NULL);
+ register_spi_master(&spi_master_ch341a_spi);
+
+ return 0;
+
+dealloc_transfers:
+ for (i = 0; i < USB_IN_TRANSFERS; i++) {
+ if (transfer_ins[i] == NULL)
+ break;
+ libusb_free_transfer(transfer_ins[i]);
+ transfer_ins[i] = NULL;
+ }
+ libusb_free_transfer(transfer_out);
+ transfer_out = NULL;
+release_interface:
+ libusb_release_interface(handle, 0);
+close_handle:
+ libusb_close(handle);
+ handle = NULL;
+ return -1;
+}
diff --git a/chipdrivers.h b/chipdrivers.h
new file mode 100644
index 0000000..c85eac9
--- /dev/null
+++ b/chipdrivers.h
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *
+ * Header file for flash chip drivers. Included from flash.h.
+ * As a general rule, every function listed here should take a pointer to
+ * struct flashctx as first parameter.
+ */
+
+#ifndef __CHIPDRIVERS_H__
+#define __CHIPDRIVERS_H__ 1
+
+#include "flash.h" /* for chipaddr and flashctx */
+
+/* spi.c */
+int spi_aai_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, int unsigned len);
+
+/* spi25.c */
+int probe_spi_rdid(struct flashctx *flash);
+int probe_spi_rdid4(struct flashctx *flash);
+int probe_spi_rems(struct flashctx *flash);
+int probe_spi_res1(struct flashctx *flash);
+int probe_spi_res2(struct flashctx *flash);
+int probe_spi_res3(struct flashctx *flash);
+int probe_spi_at25f(struct flashctx *flash);
+int spi_write_enable(struct flashctx *flash);
+int spi_write_disable(struct flashctx *flash);
+int spi_block_erase_20(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_50(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_52(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_60(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_62(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_81(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_c4(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d7(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode);
+int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int spi_byte_program(struct flashctx *flash, unsigned int addr, uint8_t databyte);
+int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len);
+int spi_nbyte_read(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len);
+int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
+int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize);
+
+/* spi25_statusreg.c */
+uint8_t spi_read_status_register(struct flashctx *flash);
+int spi_write_status_register(struct flashctx *flash, int status);
+void spi_prettyprint_status_register_bit(uint8_t status, int bit);
+int spi_prettyprint_status_register_plain(struct flashctx *flash);
+int spi_prettyprint_status_register_default_welwip(struct flashctx *flash);
+int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash);
+int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash);
+int spi_disable_blockprotect(struct flashctx *flash);
+int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash);
+int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash);
+int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash);
+int spi_disable_blockprotect_bp4_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_amic_a25l032(struct flashctx *flash);
+int spi_prettyprint_status_register_at25df(struct flashctx *flash);
+int spi_prettyprint_status_register_at25df_sec(struct flashctx *flash);
+int spi_prettyprint_status_register_at25f(struct flashctx *flash);
+int spi_prettyprint_status_register_at25f512a(struct flashctx *flash);
+int spi_prettyprint_status_register_at25f512b(struct flashctx *flash);
+int spi_prettyprint_status_register_at25f4096(struct flashctx *flash);
+int spi_prettyprint_status_register_at25fs010(struct flashctx *flash);
+int spi_prettyprint_status_register_at25fs040(struct flashctx *flash);
+int spi_prettyprint_status_register_at26df081a(struct flashctx *flash);
+int spi_disable_blockprotect_at2x_global_unprotect(struct flashctx *flash);
+int spi_disable_blockprotect_at2x_global_unprotect_sec(struct flashctx *flash);
+int spi_disable_blockprotect_at25f(struct flashctx *flash);
+int spi_disable_blockprotect_at25f512a(struct flashctx *flash);
+int spi_disable_blockprotect_at25f512b(struct flashctx *flash);
+int spi_disable_blockprotect_at25fs010(struct flashctx *flash);
+int spi_disable_blockprotect_at25fs040(struct flashctx *flash);
+int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash);
+int spi_prettyprint_status_register_n25q(struct flashctx *flash);
+int spi_disable_blockprotect_n25q(struct flashctx *flash);
+int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash);
+int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash);
+int spi_prettyprint_status_register_sst25(struct flashctx *flash);
+int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash);
+int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash);
+
+/* sfdp.c */
+int probe_spi_sfdp(struct flashctx *flash);
+
+/* opaque.c */
+int probe_opaque(struct flashctx *flash);
+int read_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int write_opaque(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int erase_opaque(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen);
+
+/* at45db.c */
+int probe_spi_at45db(struct flashctx *flash);
+int spi_prettyprint_status_register_at45db(struct flashctx *flash);
+int spi_disable_blockprotect_at45db(struct flashctx *flash);
+int spi_read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int spi_read_at45db_e8(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int spi_write_at45db(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int spi_erase_at45db_page(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_erase_at45db_block(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_erase_at45db_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_erase_at45db_chip(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int spi_erase_at45cs_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
+/* 82802ab.c */
+uint8_t wait_82802ab(struct flashctx *flash);
+int probe_82802ab(struct flashctx *flash);
+int erase_block_82802ab(struct flashctx *flash, unsigned int page, unsigned int pagesize);
+int write_82802ab(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+void print_status_82802ab(uint8_t status);
+int unlock_28f004s5(struct flashctx *flash);
+int unlock_lh28f008bjt(struct flashctx *flash);
+
+/* jedec.c */
+uint8_t oddparity(uint8_t val);
+void toggle_ready_jedec(const struct flashctx *flash, chipaddr dst);
+void data_polling_jedec(const struct flashctx *flash, chipaddr dst, uint8_t data);
+int probe_jedec(struct flashctx *flash);
+int probe_jedec_29gl(struct flashctx *flash);
+int write_jedec(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int write_jedec_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int erase_sector_jedec(struct flashctx *flash, unsigned int page, unsigned int pagesize);
+int erase_block_jedec(struct flashctx *flash, unsigned int page, unsigned int blocksize);
+int erase_chip_block_jedec(struct flashctx *flash, unsigned int page, unsigned int blocksize);
+
+int unlock_regspace2_uniform_32k(struct flashctx *flash);
+int unlock_regspace2_uniform_64k(struct flashctx *flash);
+int unlock_regspace2_block_eraser_0(struct flashctx *flash);
+int unlock_regspace2_block_eraser_1(struct flashctx *flash);
+int printlock_regspace2_uniform_64k(struct flashctx *flash);
+int printlock_regspace2_block_eraser_0(struct flashctx *flash);
+int printlock_regspace2_block_eraser_1(struct flashctx *flash);
+
+/* sst28sf040.c */
+int erase_chip_28sf040(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+int erase_sector_28sf040(struct flashctx *flash, unsigned int address, unsigned int sector_size);
+int write_28sf040(struct flashctx *flash, const uint8_t *buf,unsigned int start, unsigned int len);
+int unprotect_28sf040(struct flashctx *flash);
+int protect_28sf040(struct flashctx *flash);
+
+/* sst49lfxxxc.c */
+int erase_sector_49lfxxxc(struct flashctx *flash, unsigned int address, unsigned int sector_size);
+
+/* sst_fwhub.c */
+int printlock_sst_fwhub(struct flashctx *flash);
+int unlock_sst_fwhub(struct flashctx *flash);
+
+/* w39.c */
+int printlock_w39f010(struct flashctx * flash);
+int printlock_w39l010(struct flashctx * flash);
+int printlock_w39l020(struct flashctx * flash);
+int printlock_w39l040(struct flashctx * flash);
+int printlock_w39v040a(struct flashctx *flash);
+int printlock_w39v040b(struct flashctx *flash);
+int printlock_w39v040c(struct flashctx *flash);
+int printlock_w39v040fa(struct flashctx *flash);
+int printlock_w39v040fb(struct flashctx *flash);
+int printlock_w39v040fc(struct flashctx *flash);
+int printlock_w39v080a(struct flashctx *flash);
+int printlock_w39v080fa(struct flashctx *flash);
+int printlock_w39v080fa_dual(struct flashctx *flash);
+int printlock_at49f(struct flashctx *flash);
+
+/* w29ee011.c */
+int probe_w29ee011(struct flashctx *flash);
+
+/* stm50.c */
+int erase_sector_stm50(struct flashctx *flash, unsigned int block, unsigned int blocksize);
+
+/* en29lv640b.c */
+int probe_en29lv640b(struct flashctx *flash);
+int write_en29lv640b(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+
+#endif /* !__CHIPDRIVERS_H__ */
diff --git a/chipset_enable.c b/chipset_enable.c
new file mode 100644
index 0000000..b181b93
--- /dev/null
+++ b/chipset_enable.c
@@ -0,0 +1,1854 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2007,2008,2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Kontron Modular Computers GmbH
+ * Copyright (C) 2011, 2012 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the chipset specific flash enables.
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define NOT_DONE_YET 1
+
+#if defined(__i386__) || defined(__x86_64__)
+
+static int enable_flash_ali_m1533(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+
+ /*
+ * ROM Write enable, 0xFFFC0000-0xFFFDFFFF and
+ * 0xFFFE0000-0xFFFFFFFF ROM select enable.
+ */
+ tmp = pci_read_byte(dev, 0x47);
+ tmp |= 0x46;
+ rpci_write_byte(dev, 0x47, tmp);
+
+ return 0;
+}
+
+static int enable_flash_rdc_r8610(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+
+ /* enable ROMCS for writes */
+ tmp = pci_read_byte(dev, 0x43);
+ tmp |= 0x80;
+ pci_write_byte(dev, 0x43, tmp);
+
+ /* read the bootstrapping register */
+ tmp = pci_read_byte(dev, 0x40) & 0x3;
+ switch (tmp) {
+ case 3:
+ internal_buses_supported = BUS_FWH;
+ break;
+ case 2:
+ internal_buses_supported = BUS_LPC;
+ break;
+ default:
+ internal_buses_supported = BUS_PARALLEL;
+ break;
+ }
+
+ return 0;
+}
+
+static int enable_flash_sis85c496(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+
+ tmp = pci_read_byte(dev, 0xd0);
+ tmp |= 0xf8;
+ rpci_write_byte(dev, 0xd0, tmp);
+
+ return 0;
+}
+
+static int enable_flash_sis_mapping(struct pci_dev *dev, const char *name)
+{
+ #define SIS_MAPREG 0x40
+ uint8_t new, newer;
+
+ /* Extended BIOS enable = 1, Lower BIOS Enable = 1 */
+ /* This is 0xFFF8000~0xFFFF0000 decoding on SiS 540/630. */
+ new = pci_read_byte(dev, SIS_MAPREG);
+ new &= (~0x04); /* No idea why we clear bit 2. */
+ new |= 0xb; /* 0x3 for some chipsets, bit 7 seems to be don't care. */
+ rpci_write_byte(dev, SIS_MAPREG, new);
+ newer = pci_read_byte(dev, SIS_MAPREG);
+ if (newer != new) { /* FIXME: share this with other code? */
+ msg_pinfo("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n",
+ SIS_MAPREG, new, name);
+ msg_pinfo("Stuck at 0x%02x.\n", newer);
+ return -1;
+ }
+ return 0;
+}
+
+static struct pci_dev *find_southbridge(uint16_t vendor, const char *name)
+{
+ struct pci_dev *sbdev;
+
+ sbdev = pci_dev_find_vendorclass(vendor, 0x0601);
+ if (!sbdev)
+ sbdev = pci_dev_find_vendorclass(vendor, 0x0680);
+ if (!sbdev)
+ sbdev = pci_dev_find_vendorclass(vendor, 0x0000);
+ if (!sbdev)
+ msg_perr("No southbridge found for %s!\n", name);
+ if (sbdev)
+ msg_pdbg("Found southbridge %04x:%04x at %02x:%02x:%01x\n",
+ sbdev->vendor_id, sbdev->device_id,
+ sbdev->bus, sbdev->dev, sbdev->func);
+ return sbdev;
+}
+
+static int enable_flash_sis501(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+ int ret = 0;
+ struct pci_dev *sbdev;
+
+ sbdev = find_southbridge(dev->vendor_id, name);
+ if (!sbdev)
+ return -1;
+
+ ret = enable_flash_sis_mapping(sbdev, name);
+
+ tmp = sio_read(0x22, 0x80);
+ tmp &= (~0x20);
+ tmp |= 0x4;
+ sio_write(0x22, 0x80, tmp);
+
+ tmp = sio_read(0x22, 0x70);
+ tmp &= (~0x20);
+ tmp |= 0x4;
+ sio_write(0x22, 0x70, tmp);
+
+ return ret;
+}
+
+static int enable_flash_sis5511(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+ int ret = 0;
+ struct pci_dev *sbdev;
+
+ sbdev = find_southbridge(dev->vendor_id, name);
+ if (!sbdev)
+ return -1;
+
+ ret = enable_flash_sis_mapping(sbdev, name);
+
+ tmp = sio_read(0x22, 0x50);
+ tmp &= (~0x20);
+ tmp |= 0x4;
+ sio_write(0x22, 0x50, tmp);
+
+ return ret;
+}
+
+static int enable_flash_sis5x0(struct pci_dev *dev, const char *name, uint8_t dis_mask, uint8_t en_mask)
+{
+ #define SIS_REG 0x45
+ uint8_t new, newer;
+ int ret = 0;
+ struct pci_dev *sbdev;
+
+ sbdev = find_southbridge(dev->vendor_id, name);
+ if (!sbdev)
+ return -1;
+
+ ret = enable_flash_sis_mapping(sbdev, name);
+
+ new = pci_read_byte(sbdev, SIS_REG);
+ new &= (~dis_mask);
+ new |= en_mask;
+ rpci_write_byte(sbdev, SIS_REG, new);
+ newer = pci_read_byte(sbdev, SIS_REG);
+ if (newer != new) { /* FIXME: share this with other code? */
+ msg_pinfo("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", SIS_REG, new, name);
+ msg_pinfo("Stuck at 0x%02x\n", newer);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int enable_flash_sis530(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_sis5x0(dev, name, 0x20, 0x04);
+}
+
+static int enable_flash_sis540(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_sis5x0(dev, name, 0x80, 0x40);
+}
+
+/* Datasheet:
+ * - Name: 82371AB PCI-TO-ISA / IDE XCELERATOR (PIIX4)
+ * - URL: http://www.intel.com/design/intarch/datashts/290562.htm
+ * - PDF: http://www.intel.com/design/intarch/datashts/29056201.pdf
+ * - Order Number: 290562-001
+ */
+static int enable_flash_piix4(struct pci_dev *dev, const char *name)
+{
+ uint16_t old, new;
+ uint16_t xbcs = 0x4e; /* X-Bus Chip Select register. */
+
+ internal_buses_supported = BUS_PARALLEL;
+
+ old = pci_read_word(dev, xbcs);
+
+ /* Set bit 9: 1-Meg Extended BIOS Enable (PCI master accesses to
+ * FFF00000-FFF7FFFF are forwarded to ISA).
+ * Note: This bit is reserved on PIIX/PIIX3/MPIIX.
+ * Set bit 7: Extended BIOS Enable (PCI master accesses to
+ * FFF80000-FFFDFFFF are forwarded to ISA).
+ * Set bit 6: Lower BIOS Enable (PCI master, or ISA master accesses to
+ * the lower 64-Kbyte BIOS block (E0000-EFFFF) at the top
+ * of 1 Mbyte, or the aliases at the top of 4 Gbyte
+ * (FFFE0000-FFFEFFFF) result in the generation of BIOSCS#.
+ * Note: Accesses to FFFF0000-FFFFFFFF are always forwarded to ISA.
+ * Set bit 2: BIOSCS# Write Enable (1=enable, 0=disable).
+ */
+ if (dev->device_id == 0x122e || dev->device_id == 0x7000
+ || dev->device_id == 0x1234)
+ new = old | 0x00c4; /* PIIX/PIIX3/MPIIX: Bit 9 is reserved. */
+ else
+ new = old | 0x02c4;
+
+ if (new == old)
+ return 0;
+
+ rpci_write_word(dev, xbcs, new);
+
+ if (pci_read_word(dev, xbcs) != new) { /* FIXME: share this with other code? */
+ msg_pinfo("Setting register 0x%04x to 0x%04x on %s failed (WARNING ONLY).\n", xbcs, new, name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Handle BIOS_CNTL (aka. BCR). Disable locks and enable writes. The register can either be in PCI config space
+ * at the offset given by 'bios_cntl' or at the memory-mapped address 'addr'.
+ *
+ * Note: the ICH0-ICH5 BIOS_CNTL register is actually 16 bit wide, in Poulsbo, Tunnel Creek and other Atom
+ * chipsets/SoCs it is even 32b, but just treating it as 8 bit wide seems to work fine in practice. */
+static int enable_flash_ich_bios_cntl_common(enum ich_chipset ich_generation, void *addr,
+ struct pci_dev *dev, uint8_t bios_cntl)
+{
+ uint8_t old, new, wanted;
+
+ switch (ich_generation) {
+ case CHIPSET_ICH_UNKNOWN:
+ return ERROR_FATAL;
+ /* Non-SPI-capable */
+ case CHIPSET_ICH:
+ case CHIPSET_ICH2345:
+ break;
+ /* Some Atom chipsets are special: The second byte of BIOS_CNTL (D9h) contains a prefetch bit similar to
+ * what other SPI-capable chipsets have at DCh. Others like Bay Trail use a memmapped register.
+ * The Tunnel Creek datasheet contains a lot of details about the SPI controller, among other things it
+ * mentions that the prefetching and caching does only happen for direct memory reads.
+ * Therefore - at least for Tunnel Creek - it should not matter to flashrom because we use the
+ * programmed access only and not memory mapping. */
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_POULSBO:
+ case CHIPSET_CENTERTON:
+ old = pci_read_byte(dev, bios_cntl + 1);
+ msg_pdbg("BIOS Prefetch Enable: %sabled, ", (old & 1) ? "en" : "dis");
+ break;
+ case CHIPSET_BAYTRAIL:
+ case CHIPSET_ICH7:
+ default: /* Future version might behave the same */
+ if (ich_generation == CHIPSET_BAYTRAIL)
+ old = (mmio_readl(addr) >> 2) & 0x3;
+ else
+ old = (pci_read_byte(dev, bios_cntl) >> 2) & 0x3;
+ msg_pdbg("SPI Read Configuration: ");
+ if (old == 3)
+ msg_pdbg("invalid prefetching/caching settings, ");
+ else
+ msg_pdbg("prefetching %sabled, caching %sabled, ",
+ (old & 0x2) ? "en" : "dis",
+ (old & 0x1) ? "dis" : "en");
+ }
+
+ if (ich_generation == CHIPSET_BAYTRAIL)
+ wanted = old = mmio_readl(addr);
+ else
+ wanted = old = pci_read_byte(dev, bios_cntl);
+
+ /*
+ * Quote from the 6 Series datasheet (Document Number: 324645-004):
+ * "Bit 5: SMM BIOS Write Protect Disable (SMM_BWP)
+ * 1 = BIOS region SMM protection is enabled.
+ * The BIOS Region is not writable unless all processors are in SMM."
+ * In earlier chipsets this bit is reserved.
+ *
+ * Try to unset it in any case.
+ * It won't hurt and makes sense in some cases according to Stefan Reinauer.
+ *
+ * At least in Centerton aforementioned bit is located at bit 7. It is unspecified in all other Atom
+ * and Desktop chipsets before Ibex Peak/5 Series, but we reset bit 5 anyway.
+ */
+ int smm_bwp_bit;
+ if (ich_generation == CHIPSET_CENTERTON)
+ smm_bwp_bit = 7;
+ else
+ smm_bwp_bit = 5;
+ wanted &= ~(1 << smm_bwp_bit);
+
+ /* Tunnel Creek has a cache disable at bit 2 of the lowest BIOS_CNTL byte. */
+ if (ich_generation == CHIPSET_TUNNEL_CREEK)
+ wanted |= (1 << 2);
+
+ wanted |= (1 << 0); /* Set BIOS Write Enable */
+ wanted &= ~(1 << 1); /* Disable lock (futile) */
+
+ /* Only write the register if it's necessary */
+ if (wanted != old) {
+ if (ich_generation == CHIPSET_BAYTRAIL) {
+ rmmio_writel(wanted, addr);
+ new = mmio_readl(addr);
+ } else {
+ rpci_write_byte(dev, bios_cntl, wanted);
+ new = pci_read_byte(dev, bios_cntl);
+ }
+ } else
+ new = old;
+
+ msg_pdbg("\nBIOS_CNTL = 0x%02x: ", new);
+ msg_pdbg("BIOS Lock Enable: %sabled, ", (new & (1 << 1)) ? "en" : "dis");
+ msg_pdbg("BIOS Write Enable: %sabled\n", (new & (1 << 0)) ? "en" : "dis");
+ if (new & (1 << smm_bwp_bit))
+ msg_pwarn("Warning: BIOS region SMM protection is enabled!\n");
+
+ if (new != wanted)
+ msg_pwarn("Warning: Setting Bios Control at 0x%x from 0x%02x to 0x%02x failed.\n"
+ "New value is 0x%02x.\n", bios_cntl, old, wanted, new);
+
+ /* Return an error if we could not set the write enable only. */
+ if (!(new & (1 << 0)))
+ return -1;
+
+ return 0;
+}
+
+static int enable_flash_ich_bios_cntl_config_space(struct pci_dev *dev, enum ich_chipset ich_generation,
+ uint8_t bios_cntl)
+{
+ return enable_flash_ich_bios_cntl_common(ich_generation, NULL, dev, bios_cntl);
+}
+
+static int enable_flash_ich_bios_cntl_memmapped(enum ich_chipset ich_generation, void *addr)
+{
+ return enable_flash_ich_bios_cntl_common(ich_generation, addr, NULL, 0);
+}
+
+static int enable_flash_ich_fwh_decode(struct pci_dev *dev, enum ich_chipset ich_generation)
+{
+ uint8_t fwh_sel1 = 0, fwh_sel2 = 0, fwh_dec_en_lo = 0, fwh_dec_en_hi = 0; /* silence compilers */
+ bool implemented = 0;
+ void *ilb = NULL; /* Only for Baytrail */
+ switch (ich_generation) {
+ case CHIPSET_ICH:
+ /* FIXME: Unlike later chipsets, ICH and ICH-0 do only support mapping of the top-most 4MB
+ * and therefore do only feature FWH_DEC_EN (E3h, different default too) and FWH_SEL (E8h). */
+ break;
+ case CHIPSET_ICH2345:
+ fwh_sel1 = 0xe8;
+ fwh_sel2 = 0xee;
+ fwh_dec_en_lo = 0xf0;
+ fwh_dec_en_hi = 0xe3;
+ implemented = 1;
+ break;
+ case CHIPSET_POULSBO:
+ case CHIPSET_TUNNEL_CREEK:
+ /* FIXME: Similar to ICH and ICH-0, Tunnel Creek and Poulsbo do only feature one register each,
+ * FWH_DEC_EN (D7h) and FWH_SEL (D0h). */
+ break;
+ case CHIPSET_CENTERTON:
+ /* FIXME: Similar to above FWH_DEC_EN (D4h) and FWH_SEL (D0h). */
+ break;
+ case CHIPSET_BAYTRAIL: {
+ uint32_t ilb_base = pci_read_long(dev, 0x50) & 0xfffffe00; /* bits 31:9 */
+ if (ilb_base == 0) {
+ msg_perr("Error: Invalid ILB_BASE_ADDRESS\n");
+ return ERROR_FATAL;
+ }
+ ilb = rphysmap("BYT IBASE", ilb_base, 512);
+ fwh_sel1 = 0x18;
+ fwh_dec_en_lo = 0xd8;
+ fwh_dec_en_hi = 0xd9;
+ implemented = 1;
+ break;
+ }
+ case CHIPSET_ICH6:
+ case CHIPSET_ICH7:
+ default: /* Future version might behave the same */
+ fwh_sel1 = 0xd0;
+ fwh_sel2 = 0xd4;
+ fwh_dec_en_lo = 0xd8;
+ fwh_dec_en_hi = 0xd9;
+ implemented = 1;
+ break;
+ }
+
+ char *idsel = extract_programmer_param("fwh_idsel");
+ if (idsel && strlen(idsel)) {
+ if (!implemented) {
+ msg_perr("Error: fwh_idsel= specified, but (yet) unsupported on this chipset.\n");
+ goto idsel_garbage_out;
+ }
+ errno = 0;
+ /* Base 16, nothing else makes sense. */
+ uint64_t fwh_idsel = (uint64_t)strtoull(idsel, NULL, 16);
+ if (errno) {
+ msg_perr("Error: fwh_idsel= specified, but value could not be converted.\n");
+ goto idsel_garbage_out;
+ }
+ uint64_t fwh_mask = 0xffffffff;
+ if (fwh_sel2 > 0)
+ fwh_mask |= (0xffffULL << 32);
+ if (fwh_idsel & ~fwh_mask) {
+ msg_perr("Error: fwh_idsel= specified, but value had unused bits set.\n");
+ goto idsel_garbage_out;
+ }
+ uint64_t fwh_idsel_old;
+ if (ich_generation == CHIPSET_BAYTRAIL) {
+ fwh_idsel_old = mmio_readl(ilb + fwh_sel1);
+ rmmio_writel(fwh_idsel, ilb + fwh_sel1);
+ } else {
+ fwh_idsel_old = (uint64_t)pci_read_long(dev, fwh_sel1) << 16;
+ rpci_write_long(dev, fwh_sel1, (fwh_idsel >> 16) & 0xffffffff);
+ if (fwh_sel2 > 0) {
+ fwh_idsel_old |= pci_read_word(dev, fwh_sel2);
+ rpci_write_word(dev, fwh_sel2, fwh_idsel & 0xffff);
+ }
+ }
+ msg_pdbg("Setting IDSEL from 0x%012" PRIx64 " to 0x%012" PRIx64 " for top 16 MB.\n",
+ fwh_idsel_old, fwh_idsel);
+ /* FIXME: Decode settings are not changed. */
+ } else if (idsel) {
+ msg_perr("Error: fwh_idsel= specified, but no value given.\n");
+idsel_garbage_out:
+ free(idsel);
+ return ERROR_FATAL;
+ }
+ free(idsel);
+
+ if (!implemented) {
+ msg_pdbg2("FWH IDSEL handling is not implemented on this chipset.\n");
+ return 0;
+ }
+
+ /* Ignore all legacy ranges below 1 MB.
+ * We currently only support flashing the chip which responds to
+ * IDSEL=0. To support IDSEL!=0, flashbase and decode size calculations
+ * have to be adjusted.
+ */
+ int max_decode_fwh_idsel = 0, max_decode_fwh_decode = 0;
+ bool contiguous = 1;
+ uint32_t fwh_conf;
+ if (ich_generation == CHIPSET_BAYTRAIL)
+ fwh_conf = mmio_readl(ilb + fwh_sel1);
+ else
+ fwh_conf = pci_read_long(dev, fwh_sel1);
+
+ int i;
+ /* FWH_SEL1 */
+ for (i = 7; i >= 0; i--) {
+ int tmp = (fwh_conf >> (i * 4)) & 0xf;
+ msg_pdbg("0x%08x/0x%08x FWH IDSEL: 0x%x\n",
+ (0x1ff8 + i) * 0x80000,
+ (0x1ff0 + i) * 0x80000,
+ tmp);
+ if ((tmp == 0) && contiguous) {
+ max_decode_fwh_idsel = (8 - i) * 0x80000;
+ } else {
+ contiguous = 0;
+ }
+ }
+ if (fwh_sel2 > 0) {
+ /* FWH_SEL2 */
+ fwh_conf = pci_read_word(dev, fwh_sel2);
+ for (i = 3; i >= 0; i--) {
+ int tmp = (fwh_conf >> (i * 4)) & 0xf;
+ msg_pdbg("0x%08x/0x%08x FWH IDSEL: 0x%x\n",
+ (0xff4 + i) * 0x100000,
+ (0xff0 + i) * 0x100000,
+ tmp);
+ if ((tmp == 0) && contiguous) {
+ max_decode_fwh_idsel = (8 - i) * 0x100000;
+ } else {
+ contiguous = 0;
+ }
+ }
+ }
+ contiguous = 1;
+ /* FWH_DEC_EN1 */
+ fwh_conf = pci_read_byte(dev, fwh_dec_en_hi);
+ fwh_conf <<= 8;
+ fwh_conf |= pci_read_byte(dev, fwh_dec_en_lo);
+ for (i = 7; i >= 0; i--) {
+ int tmp = (fwh_conf >> (i + 0x8)) & 0x1;
+ msg_pdbg("0x%08x/0x%08x FWH decode %sabled\n",
+ (0x1ff8 + i) * 0x80000,
+ (0x1ff0 + i) * 0x80000,
+ tmp ? "en" : "dis");
+ if ((tmp == 1) && contiguous) {
+ max_decode_fwh_decode = (8 - i) * 0x80000;
+ } else {
+ contiguous = 0;
+ }
+ }
+ for (i = 3; i >= 0; i--) {
+ int tmp = (fwh_conf >> i) & 0x1;
+ msg_pdbg("0x%08x/0x%08x FWH decode %sabled\n",
+ (0xff4 + i) * 0x100000,
+ (0xff0 + i) * 0x100000,
+ tmp ? "en" : "dis");
+ if ((tmp == 1) && contiguous) {
+ max_decode_fwh_decode = (8 - i) * 0x100000;
+ } else {
+ contiguous = 0;
+ }
+ }
+ max_rom_decode.fwh = min(max_decode_fwh_idsel, max_decode_fwh_decode);
+ msg_pdbg("Maximum FWH chip size: 0x%x bytes\n", max_rom_decode.fwh);
+
+ return 0;
+}
+
+static int enable_flash_ich_fwh(struct pci_dev *dev, enum ich_chipset ich_generation, uint8_t bios_cntl)
+{
+ int err;
+
+ /* Configure FWH IDSEL decoder maps. */
+ if ((err = enable_flash_ich_fwh_decode(dev, ich_generation)) != 0)
+ return err;
+
+ internal_buses_supported = BUS_FWH;
+ return enable_flash_ich_bios_cntl_config_space(dev, ich_generation, bios_cntl);
+}
+
+static int enable_flash_ich0(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_fwh(dev, CHIPSET_ICH, 0x4e);
+}
+
+static int enable_flash_ich2345(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_fwh(dev, CHIPSET_ICH2345, 0x4e);
+}
+
+static int enable_flash_ich6(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_fwh(dev, CHIPSET_ICH6, 0xdc);
+}
+
+static int enable_flash_poulsbo(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_fwh(dev, CHIPSET_POULSBO, 0xd8);
+}
+
+static void enable_flash_ich_handle_gcs(struct pci_dev *dev, enum ich_chipset ich_generation, uint32_t gcs, bool top_swap)
+{
+ msg_pdbg("GCS = 0x%x: ", gcs);
+ msg_pdbg("BIOS Interface Lock-Down: %sabled, ", (gcs & 0x1) ? "en" : "dis");
+
+ static const char *const straps_names_EP80579[] = { "SPI", "reserved", "reserved", "LPC" };
+ static const char *const straps_names_ich7_nm10[] = { "reserved", "SPI", "PCI", "LPC" };
+ static const char *const straps_names_tunnel_creek[] = { "SPI", "LPC" };
+ static const char *const straps_names_ich8910[] = { "SPI", "SPI", "PCI", "LPC" };
+ static const char *const straps_names_pch567[] = { "LPC", "reserved", "PCI", "SPI" };
+ static const char *const straps_names_pch89_baytrail[] = { "LPC", "reserved", "reserved", "SPI" };
+ static const char *const straps_names_pch8_lp[] = { "SPI", "LPC" };
+ static const char *const straps_names_unknown[] = { "unknown", "unknown", "unknown", "unknown" };
+
+ const char *const *straps_names;
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ /* EP80579 may need further changes, but this is the least
+ * intrusive way to get correct BOOT Strap printing without
+ * changing the rest of its code path). */
+ if (dev->device_id == 0x5031)
+ straps_names = straps_names_EP80579;
+ else
+ straps_names = straps_names_ich7_nm10;
+ break;
+ case CHIPSET_ICH8:
+ case CHIPSET_ICH9:
+ case CHIPSET_ICH10:
+ straps_names = straps_names_ich8910;
+ break;
+ case CHIPSET_TUNNEL_CREEK:
+ straps_names = straps_names_tunnel_creek;
+ break;
+ case CHIPSET_5_SERIES_IBEX_PEAK:
+ case CHIPSET_6_SERIES_COUGAR_POINT:
+ case CHIPSET_7_SERIES_PANTHER_POINT:
+ straps_names = straps_names_pch567;
+ break;
+ case CHIPSET_8_SERIES_LYNX_POINT:
+ case CHIPSET_9_SERIES_WILDCAT_POINT:
+ case CHIPSET_BAYTRAIL:
+ straps_names = straps_names_pch89_baytrail;
+ break;
+ case CHIPSET_8_SERIES_LYNX_POINT_LP:
+ straps_names = straps_names_pch8_lp;
+ break;
+ case CHIPSET_8_SERIES_WELLSBURG: // FIXME: check datasheet
+ case CHIPSET_CENTERTON: // FIXME: Datasheet does not mention GCS at all
+ straps_names = straps_names_unknown;
+ break;
+ default:
+ msg_gerr("%s: unknown ICH generation. Please report!\n", __func__);
+ straps_names = straps_names_unknown;
+ break;
+ }
+
+ uint8_t bbs;
+ switch (ich_generation) {
+ case CHIPSET_TUNNEL_CREEK:
+ bbs = (gcs >> 1) & 0x1;
+ break;
+ case CHIPSET_8_SERIES_LYNX_POINT_LP:
+ /* Lynx Point LP uses a single bit for BBS */
+ bbs = (gcs >> 10) & 0x1;
+ break;
+ default:
+ /* Other chipsets use two bits for BBS */
+ bbs = (gcs >> 10) & 0x3;
+ break;
+ }
+ msg_pdbg("Boot BIOS Straps: 0x%x (%s)\n", bbs, straps_names[bbs]);
+
+ /* Centerton has its TS bit in [GPE0BLK] + 0x30 while the exact location for Tunnel Creek is unknown. */
+ if (ich_generation != CHIPSET_TUNNEL_CREEK && ich_generation != CHIPSET_CENTERTON)
+ msg_pdbg("Top Swap: %s\n", (top_swap) ? "enabled (A16(+) inverted)" : "not enabled");
+}
+
+static int enable_flash_ich_spi(struct pci_dev *dev, enum ich_chipset ich_generation, uint8_t bios_cntl)
+{
+
+ /* Get physical address of Root Complex Register Block */
+ uint32_t rcra = pci_read_long(dev, 0xf0) & 0xffffc000;
+ msg_pdbg("Root Complex Register Block address = 0x%x\n", rcra);
+
+ /* Map RCBA to virtual memory */
+ void *rcrb = rphysmap("ICH RCRB", rcra, 0x4000);
+ if (rcrb == ERROR_PTR)
+ return ERROR_FATAL;
+
+ enable_flash_ich_handle_gcs(dev, ich_generation, mmio_readl(rcrb + 0x3410), mmio_readb(rcrb + 0x3414));
+
+ /* Handle FWH-related parameters and initialization */
+ int ret_fwh = enable_flash_ich_fwh(dev, ich_generation, bios_cntl);
+ if (ret_fwh == ERROR_FATAL)
+ return ret_fwh;
+
+ /* SPIBAR is at RCRB+0x3020 for ICH[78], Tunnel Creek and Centerton, and RCRB+0x3800 for ICH9. */
+ uint16_t spibar_offset;
+ switch (ich_generation) {
+ case CHIPSET_BAYTRAIL:
+ case CHIPSET_ICH_UNKNOWN:
+ return ERROR_FATAL;
+ case CHIPSET_ICH7:
+ case CHIPSET_ICH8:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ spibar_offset = 0x3020;
+ break;
+ case CHIPSET_ICH9:
+ default: /* Future version might behave the same */
+ spibar_offset = 0x3800;
+ break;
+ }
+ msg_pdbg("SPIBAR = 0x%0*" PRIxPTR " + 0x%04x\n", PRIxPTR_WIDTH, (uintptr_t)rcrb, spibar_offset);
+ void *spibar = rcrb + spibar_offset;
+
+ /* This adds BUS_SPI */
+ int ret_spi = ich_init_spi(dev, spibar, ich_generation);
+ if (ret_spi == ERROR_FATAL)
+ return ret_spi;
+
+ if (ret_fwh || ret_spi)
+ return ERROR_NONFATAL;
+
+ return 0;
+}
+
+static int enable_flash_tunnelcreek(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_TUNNEL_CREEK, 0xd8);
+}
+
+static int enable_flash_s12x0(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_CENTERTON, 0xd8);
+}
+
+static int enable_flash_ich7(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_ICH7, 0xdc);
+}
+
+static int enable_flash_ich8(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_ICH8, 0xdc);
+}
+
+static int enable_flash_ich9(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_ICH9, 0xdc);
+}
+
+static int enable_flash_ich10(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_ICH10, 0xdc);
+}
+
+/* Ibex Peak aka. 5 series & 3400 series */
+static int enable_flash_pch5(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_5_SERIES_IBEX_PEAK, 0xdc);
+}
+
+/* Cougar Point aka. 6 series & c200 series */
+static int enable_flash_pch6(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_6_SERIES_COUGAR_POINT, 0xdc);
+}
+
+/* Panther Point aka. 7 series */
+static int enable_flash_pch7(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_7_SERIES_PANTHER_POINT, 0xdc);
+}
+
+/* Lynx Point aka. 8 series */
+static int enable_flash_pch8(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_LYNX_POINT, 0xdc);
+}
+
+/* Lynx Point LP aka. 8 series low-power */
+static int enable_flash_pch8_lp(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_LYNX_POINT_LP, 0xdc);
+}
+
+/* Wellsburg (for Haswell-EP Xeons) */
+static int enable_flash_pch8_wb(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_8_SERIES_WELLSBURG, 0xdc);
+}
+
+/* Wildcat Point */
+static int enable_flash_pch9(struct pci_dev *dev, const char *name)
+{
+ return enable_flash_ich_spi(dev, CHIPSET_9_SERIES_WILDCAT_POINT, 0xdc);
+}
+
+/* Silvermont architecture: Bay Trail(-T/-I), Avoton/Rangeley.
+ * These have a distinctly different behavior compared to other Intel chipsets and hence are handled separately.
+ *
+ * Differences include:
+ * - RCBA at LPC config 0xF0 too but mapped range is only 4 B long instead of 16 kB.
+ * - GCS at [RCRB] + 0 (instead of [RCRB] + 0x3410).
+ * - TS (Top Swap) in GCS (instead of [RCRB] + 0x3414).
+ * - SPIBAR (coined SBASE) at LPC config 0x54 (instead of [RCRB] + 0x3800).
+ * - BIOS_CNTL (coined BCR) at [SPIBAR] + 0xFC (instead of LPC config 0xDC).
+ */
+static int enable_flash_silvermont(struct pci_dev *dev, const char *name)
+{
+ enum ich_chipset ich_generation = CHIPSET_BAYTRAIL;
+
+ /* Get physical address of Root Complex Register Block */
+ uint32_t rcba = pci_read_long(dev, 0xf0) & 0xfffffc00;
+ msg_pdbg("Root Complex Register Block address = 0x%x\n", rcba);
+
+ /* Handle GCS (in RCRB) */
+ void *rcrb = physmap("BYT RCRB", rcba, 4);
+ uint32_t gcs = mmio_readl(rcrb + 0);
+ enable_flash_ich_handle_gcs(dev, ich_generation, gcs, gcs & 0x2);
+ physunmap(rcrb, 4);
+
+ /* Handle fwh_idsel parameter */
+ int ret_fwh = enable_flash_ich_fwh_decode(dev, ich_generation);
+ if (ret_fwh == ERROR_FATAL)
+ return ret_fwh;
+
+ internal_buses_supported = BUS_FWH;
+
+ /* Get physical address of SPI Base Address and map it */
+ uint32_t sbase = pci_read_long(dev, 0x54) & 0xfffffe00;
+ msg_pdbg("SPI_BASE_ADDRESS = 0x%x\n", sbase);
+ void *spibar = rphysmap("BYT SBASE", sbase, 512); /* Last defined address on Bay Trail is 0x100 */
+
+ /* Enable Flash Writes.
+ * Silvermont-based: BCR at SBASE + 0xFC (some bits of BCR are also accessible via BC at IBASE + 0x1C).
+ */
+ enable_flash_ich_bios_cntl_memmapped(ich_generation, spibar + 0xFC);
+
+ int ret_spi = ich_init_spi(dev, spibar, ich_generation);
+ if (ret_spi == ERROR_FATAL)
+ return ret_spi;
+
+ if (ret_fwh || ret_spi)
+ return ERROR_NONFATAL;
+
+ return 0;
+}
+
+static int via_no_byte_merge(struct pci_dev *dev, const char *name)
+{
+ uint8_t val;
+
+ val = pci_read_byte(dev, 0x71);
+ if (val & 0x40) {
+ msg_pdbg("Disabling byte merging\n");
+ val &= ~0x40;
+ rpci_write_byte(dev, 0x71, val);
+ }
+ return NOT_DONE_YET; /* need to find south bridge, too */
+}
+
+static int enable_flash_vt823x(struct pci_dev *dev, const char *name)
+{
+ uint8_t val;
+
+ /* Enable ROM decode range (1MB) FFC00000 - FFFFFFFF. */
+ rpci_write_byte(dev, 0x41, 0x7f);
+
+ /* ROM write enable */
+ val = pci_read_byte(dev, 0x40);
+ val |= 0x10;
+ rpci_write_byte(dev, 0x40, val);
+
+ if (pci_read_byte(dev, 0x40) != val) {
+ msg_pwarn("\nWarning: Failed to enable flash write on \"%s\"\n", name);
+ return -1;
+ }
+
+ if (dev->device_id == 0x3227) { /* VT8237/VT8237R */
+ /* All memory cycles, not just ROM ones, go to LPC. */
+ val = pci_read_byte(dev, 0x59);
+ val &= ~0x80;
+ rpci_write_byte(dev, 0x59, val);
+ }
+
+ return 0;
+}
+
+static int enable_flash_vt_vx(struct pci_dev *dev, const char *name)
+{
+ struct pci_dev *south_north = pci_dev_find(0x1106, 0xa353);
+ if (south_north == NULL) {
+ msg_perr("Could not find South-North Module Interface Control device!\n");
+ return ERROR_FATAL;
+ }
+
+ msg_pdbg("Strapped to ");
+ if ((pci_read_byte(south_north, 0x56) & 0x01) == 0) {
+ msg_pdbg("LPC.\n");
+ return enable_flash_vt823x(dev, name);
+ }
+ msg_pdbg("SPI.\n");
+
+ uint32_t mmio_base;
+ void *mmio_base_physmapped;
+ uint32_t spi_cntl;
+ #define SPI_CNTL_LEN 0x08
+ uint32_t spi0_mm_base = 0;
+ switch(dev->device_id) {
+ case 0x8353: /* VX800/VX820 */
+ spi0_mm_base = pci_read_long(dev, 0xbc) << 8;
+ break;
+ case 0x8409: /* VX855/VX875 */
+ case 0x8410: /* VX900 */
+ mmio_base = pci_read_long(dev, 0xbc) << 8;
+ mmio_base_physmapped = physmap("VIA VX MMIO register", mmio_base, SPI_CNTL_LEN);
+ if (mmio_base_physmapped == ERROR_PTR)
+ return ERROR_FATAL;
+
+ /* Offset 0 - Bit 0 holds SPI Bus0 Enable Bit. */
+ spi_cntl = mmio_readl(mmio_base_physmapped) + 0x00;
+ if ((spi_cntl & 0x01) == 0) {
+ msg_pdbg ("SPI Bus0 disabled!\n");
+ physunmap(mmio_base_physmapped, SPI_CNTL_LEN);
+ return ERROR_FATAL;
+ }
+ /* Offset 1-3 has SPI Bus Memory Map Base Address: */
+ spi0_mm_base = spi_cntl & 0xFFFFFF00;
+
+ /* Offset 4 - Bit 0 holds SPI Bus1 Enable Bit. */
+ spi_cntl = mmio_readl(mmio_base_physmapped) + 0x04;
+ if ((spi_cntl & 0x01) == 1)
+ msg_pdbg2("SPI Bus1 is enabled too.\n");
+
+ physunmap(mmio_base_physmapped, SPI_CNTL_LEN);
+ break;
+ default:
+ msg_perr("%s: Unsupported chipset %x:%x!\n", __func__, dev->vendor_id, dev->device_id);
+ return ERROR_FATAL;
+ }
+
+ return via_init_spi(dev, spi0_mm_base);
+}
+
+static int enable_flash_vt8237s_spi(struct pci_dev *dev, const char *name)
+{
+ return via_init_spi(dev, pci_read_long(dev, 0xbc) << 8);
+}
+
+static int enable_flash_cs5530(struct pci_dev *dev, const char *name)
+{
+ uint8_t reg8;
+
+#define DECODE_CONTROL_REG2 0x5b /* F0 index 0x5b */
+#define ROM_AT_LOGIC_CONTROL_REG 0x52 /* F0 index 0x52 */
+#define CS5530_RESET_CONTROL_REG 0x44 /* F0 index 0x44 */
+#define CS5530_USB_SHADOW_REG 0x43 /* F0 index 0x43 */
+
+#define LOWER_ROM_ADDRESS_RANGE (1 << 0)
+#define ROM_WRITE_ENABLE (1 << 1)
+#define UPPER_ROM_ADDRESS_RANGE (1 << 2)
+#define BIOS_ROM_POSITIVE_DECODE (1 << 5)
+#define CS5530_ISA_MASTER (1 << 7)
+#define CS5530_ENABLE_SA2320 (1 << 2)
+#define CS5530_ENABLE_SA20 (1 << 6)
+
+ internal_buses_supported = BUS_PARALLEL;
+ /* Decode 0x000E0000-0x000FFFFF (128 kB), not just 64 kB, and
+ * decode 0xFF000000-0xFFFFFFFF (16 MB), not just 256 kB.
+ * FIXME: Should we really touch the low mapping below 1 MB? Flashrom
+ * ignores that region completely.
+ * Make the configured ROM areas writable.
+ */
+ reg8 = pci_read_byte(dev, ROM_AT_LOGIC_CONTROL_REG);
+ reg8 |= LOWER_ROM_ADDRESS_RANGE;
+ reg8 |= UPPER_ROM_ADDRESS_RANGE;
+ reg8 |= ROM_WRITE_ENABLE;
+ rpci_write_byte(dev, ROM_AT_LOGIC_CONTROL_REG, reg8);
+
+ /* Set positive decode on ROM. */
+ reg8 = pci_read_byte(dev, DECODE_CONTROL_REG2);
+ reg8 |= BIOS_ROM_POSITIVE_DECODE;
+ rpci_write_byte(dev, DECODE_CONTROL_REG2, reg8);
+
+ reg8 = pci_read_byte(dev, CS5530_RESET_CONTROL_REG);
+ if (reg8 & CS5530_ISA_MASTER) {
+ /* We have A0-A23 available. */
+ max_rom_decode.parallel = 16 * 1024 * 1024;
+ } else {
+ reg8 = pci_read_byte(dev, CS5530_USB_SHADOW_REG);
+ if (reg8 & CS5530_ENABLE_SA2320) {
+ /* We have A0-19, A20-A23 available. */
+ max_rom_decode.parallel = 16 * 1024 * 1024;
+ } else if (reg8 & CS5530_ENABLE_SA20) {
+ /* We have A0-19, A20 available. */
+ max_rom_decode.parallel = 2 * 1024 * 1024;
+ } else {
+ /* A20 and above are not active. */
+ max_rom_decode.parallel = 1024 * 1024;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Geode systems write protect the BIOS via RCONFs (cache settings similar
+ * to MTRRs). To unlock, change MSR 0x1808 top byte to 0x22.
+ *
+ * Geode systems also write protect the NOR flash chip itself via MSR_NORF_CTL.
+ * To enable write to NOR Boot flash for the benefit of systems that have such
+ * a setup, raise MSR 0x51400018 WE_CS3 (write enable Boot Flash Chip Select).
+ */
+static int enable_flash_cs5536(struct pci_dev *dev, const char *name)
+{
+#define MSR_RCONF_DEFAULT 0x1808
+#define MSR_NORF_CTL 0x51400018
+
+ msr_t msr;
+
+ /* Geode only has a single core */
+ if (setup_cpu_msr(0))
+ return -1;
+
+ msr = rdmsr(MSR_RCONF_DEFAULT);
+ if ((msr.hi >> 24) != 0x22) {
+ msr.hi &= 0xfbffffff;
+ wrmsr(MSR_RCONF_DEFAULT, msr);
+ }
+
+ msr = rdmsr(MSR_NORF_CTL);
+ /* Raise WE_CS3 bit. */
+ msr.lo |= 0x08;
+ wrmsr(MSR_NORF_CTL, msr);
+
+ cleanup_cpu_msr();
+
+#undef MSR_RCONF_DEFAULT
+#undef MSR_NORF_CTL
+ return 0;
+}
+
+static int enable_flash_sc1100(struct pci_dev *dev, const char *name)
+{
+ #define SC_REG 0x52
+ uint8_t new;
+
+ rpci_write_byte(dev, SC_REG, 0xee);
+
+ new = pci_read_byte(dev, SC_REG);
+
+ if (new != 0xee) { /* FIXME: share this with other code? */
+ msg_pinfo("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n", SC_REG, new, name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Works for AMD-768, AMD-8111, VIA VT82C586A/B, VIA VT82C596, VIA VT82C686A/B.
+ *
+ * ROM decode control register matrix
+ * AMD-768 AMD-8111 VT82C586A/B VT82C596 VT82C686A/B
+ * 7 FFC0_0000h–FFFF_FFFFh <- FFFE0000h-FFFEFFFFh <- <-
+ * 6 FFB0_0000h–FFBF_FFFFh <- FFF80000h-FFFDFFFFh <- <-
+ * 5 00E8... <- <- FFF00000h-FFF7FFFFh <-
+ */
+static int enable_flash_amd_via(struct pci_dev *dev, const char *name, uint8_t decode_val)
+{
+ #define AMD_MAPREG 0x43
+ #define AMD_ENREG 0x40
+ uint8_t old, new;
+
+ old = pci_read_byte(dev, AMD_MAPREG);
+ new = old | decode_val;
+ if (new != old) {
+ rpci_write_byte(dev, AMD_MAPREG, new);
+ if (pci_read_byte(dev, AMD_MAPREG) != new) {
+ msg_pwarn("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n",
+ AMD_MAPREG, new, name);
+ } else
+ msg_pdbg("Changed ROM decode range to 0x%02x successfully.\n", new);
+ }
+
+ /* Enable 'ROM write' bit. */
+ old = pci_read_byte(dev, AMD_ENREG);
+ new = old | 0x01;
+ if (new == old)
+ return 0;
+ rpci_write_byte(dev, AMD_ENREG, new);
+
+ if (pci_read_byte(dev, AMD_ENREG) != new) {
+ msg_pwarn("Setting register 0x%x to 0x%02x on %s failed (WARNING ONLY).\n",
+ AMD_ENREG, new, name);
+ return ERROR_NONFATAL;
+ }
+ msg_pdbg2("Set ROM enable bit successfully.\n");
+
+ return 0;
+}
+
+static int enable_flash_amd_768_8111(struct pci_dev *dev, const char *name)
+{
+ /* Enable decoding of 0xFFB00000 to 0xFFFFFFFF (5 MB). */
+ max_rom_decode.lpc = 5 * 1024 * 1024;
+ return enable_flash_amd_via(dev, name, 0xC0);
+}
+
+static int enable_flash_vt82c586(struct pci_dev *dev, const char *name)
+{
+ /* Enable decoding of 0xFFF80000 to 0xFFFFFFFF. (512 kB) */
+ max_rom_decode.parallel = 512 * 1024;
+ return enable_flash_amd_via(dev, name, 0xC0);
+}
+
+/* Works for VT82C686A/B too. */
+static int enable_flash_vt82c596(struct pci_dev *dev, const char *name)
+{
+ /* Enable decoding of 0xFFF00000 to 0xFFFFFFFF. (1 MB) */
+ max_rom_decode.parallel = 1024 * 1024;
+ return enable_flash_amd_via(dev, name, 0xE0);
+}
+
+static int enable_flash_sb600(struct pci_dev *dev, const char *name)
+{
+ uint32_t prot;
+ uint8_t reg;
+ int ret;
+
+ /* Clear ROM protect 0-3. */
+ for (reg = 0x50; reg < 0x60; reg += 4) {
+ prot = pci_read_long(dev, reg);
+ /* No protection flags for this region?*/
+ if ((prot & 0x3) == 0)
+ continue;
+ msg_pdbg("Chipset %s%sprotected flash from 0x%08x to 0x%08x, unlocking...",
+ (prot & 0x2) ? "read " : "",
+ (prot & 0x1) ? "write " : "",
+ (prot & 0xfffff800),
+ (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+ prot &= 0xfffffffc;
+ rpci_write_byte(dev, reg, prot);
+ prot = pci_read_long(dev, reg);
+ if ((prot & 0x3) != 0) {
+ msg_perr("Disabling %s%sprotection of flash addresses from 0x%08x to 0x%08x failed.\n",
+ (prot & 0x2) ? "read " : "",
+ (prot & 0x1) ? "write " : "",
+ (prot & 0xfffff800),
+ (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+ continue;
+ }
+ msg_pdbg("done.\n");
+ }
+
+ internal_buses_supported = BUS_LPC | BUS_FWH;
+
+ ret = sb600_probe_spi(dev);
+
+ /* Read ROM strap override register. */
+ OUTB(0x8f, 0xcd6);
+ reg = INB(0xcd7);
+ reg &= 0x0e;
+ msg_pdbg("ROM strap override is %sactive", (reg & 0x02) ? "" : "not ");
+ if (reg & 0x02) {
+ switch ((reg & 0x0c) >> 2) {
+ case 0x00:
+ msg_pdbg(": LPC");
+ break;
+ case 0x01:
+ msg_pdbg(": PCI");
+ break;
+ case 0x02:
+ msg_pdbg(": FWH");
+ break;
+ case 0x03:
+ msg_pdbg(": SPI");
+ break;
+ }
+ }
+ msg_pdbg("\n");
+
+ /* Force enable SPI ROM in SB600 PM register.
+ * If we enable SPI ROM here, we have to disable it after we leave.
+ * But how can we know which ROM we are going to handle? So we have
+ * to trade off. We only access LPC ROM if we boot via LPC ROM. And
+ * only SPI ROM if we boot via SPI ROM. If you want to access SPI on
+ * boards with LPC straps, you have to use the code below.
+ */
+ /*
+ OUTB(0x8f, 0xcd6);
+ OUTB(0x0e, 0xcd7);
+ */
+
+ return ret;
+}
+
+/* sets bit 0 in 0x6d */
+static int enable_flash_nvidia_common(struct pci_dev *dev, const char *name)
+{
+ uint8_t old, new;
+
+ old = pci_read_byte(dev, 0x6d);
+ new = old | 0x01;
+ if (new == old)
+ return 0;
+
+ rpci_write_byte(dev, 0x6d, new);
+ if (pci_read_byte(dev, 0x6d) != new) {
+ msg_pinfo("Setting register 0x6d to 0x%02x on %s failed.\n", new, name);
+ return 1;
+ }
+ return 0;
+}
+
+static int enable_flash_nvidia_nforce2(struct pci_dev *dev, const char *name)
+{
+ rpci_write_byte(dev, 0x92, 0);
+ if (enable_flash_nvidia_common(dev, name))
+ return ERROR_NONFATAL;
+ else
+ return 0;
+}
+
+static int enable_flash_ck804(struct pci_dev *dev, const char *name)
+{
+ uint32_t segctrl;
+ uint8_t reg, old, new;
+ unsigned int err = 0;
+
+ /* 0x8A is special: it is a single byte and only one nibble is touched. */
+ reg = 0x8A;
+ segctrl = pci_read_byte(dev, reg);
+ if ((segctrl & 0x3) != 0x0) {
+ if ((segctrl & 0xC) != 0x0) {
+ msg_pinfo("Can not unlock existing protection in register 0x%02x.\n", reg);
+ err++;
+ } else {
+ msg_pdbg("Unlocking protection in register 0x%02x... ", reg);
+ rpci_write_byte(dev, reg, segctrl & 0xF0);
+
+ segctrl = pci_read_byte(dev, reg);
+ if ((segctrl & 0x3) != 0x0) {
+ msg_pinfo("Could not unlock protection in register 0x%02x (new value: 0x%x).\n",
+ reg, segctrl);
+ err++;
+ } else
+ msg_pdbg("OK\n");
+ }
+ }
+
+ for (reg = 0x8C; reg <= 0x94; reg += 4) {
+ segctrl = pci_read_long(dev, reg);
+ if ((segctrl & 0x33333333) == 0x00000000) {
+ /* reads and writes are unlocked */
+ continue;
+ }
+ if ((segctrl & 0xCCCCCCCC) != 0x00000000) {
+ msg_pinfo("Can not unlock existing protection in register 0x%02x.\n", reg);
+ err++;
+ continue;
+ }
+ msg_pdbg("Unlocking protection in register 0x%02x... ", reg);
+ rpci_write_long(dev, reg, 0x00000000);
+
+ segctrl = pci_read_long(dev, reg);
+ if ((segctrl & 0x33333333) != 0x00000000) {
+ msg_pinfo("Could not unlock protection in register 0x%02x (new value: 0x%08x).\n",
+ reg, segctrl);
+ err++;
+ } else
+ msg_pdbg("OK\n");
+ }
+
+ if (err > 0) {
+ msg_pinfo("%d locks could not be disabled, disabling writes (reads may also fail).\n", err);
+ programmer_may_write = 0;
+ }
+
+ reg = 0x88;
+ old = pci_read_byte(dev, reg);
+ new = old | 0xC0;
+ if (new != old) {
+ rpci_write_byte(dev, reg, new);
+ if (pci_read_byte(dev, reg) != new) { /* FIXME: share this with other code? */
+ msg_pinfo("Setting register 0x%02x to 0x%02x on %s failed.\n", reg, new, name);
+ err++;
+ }
+ }
+
+ if (enable_flash_nvidia_common(dev, name))
+ err++;
+
+ if (err > 0)
+ return ERROR_NONFATAL;
+ else
+ return 0;
+}
+
+static int enable_flash_osb4(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+
+ internal_buses_supported = BUS_PARALLEL;
+
+ tmp = INB(0xc06);
+ tmp |= 0x1;
+ OUTB(tmp, 0xc06);
+
+ tmp = INB(0xc6f);
+ tmp |= 0x40;
+ OUTB(tmp, 0xc6f);
+
+ return 0;
+}
+
+/* ATI Technologies Inc IXP SB400 PCI-ISA Bridge (rev 80) */
+static int enable_flash_sb400(struct pci_dev *dev, const char *name)
+{
+ uint8_t tmp;
+ struct pci_dev *smbusdev;
+
+ /* Look for the SMBus device. */
+ smbusdev = pci_dev_find(0x1002, 0x4372);
+
+ if (!smbusdev) {
+ msg_perr("ERROR: SMBus device not found. Aborting.\n");
+ return ERROR_FATAL;
+ }
+
+ /* Enable some SMBus stuff. */
+ tmp = pci_read_byte(smbusdev, 0x79);
+ tmp |= 0x01;
+ rpci_write_byte(smbusdev, 0x79, tmp);
+
+ /* Change southbridge. */
+ tmp = pci_read_byte(dev, 0x48);
+ tmp |= 0x21;
+ rpci_write_byte(dev, 0x48, tmp);
+
+ /* Now become a bit silly. */
+ tmp = INB(0xc6f);
+ OUTB(tmp, 0xeb);
+ OUTB(tmp, 0xeb);
+ tmp |= 0x40;
+ OUTB(tmp, 0xc6f);
+ OUTB(tmp, 0xeb);
+ OUTB(tmp, 0xeb);
+
+ return 0;
+}
+
+static int enable_flash_mcp55(struct pci_dev *dev, const char *name)
+{
+ uint8_t val;
+ uint16_t wordval;
+
+ /* Set the 0-16 MB enable bits. */
+ val = pci_read_byte(dev, 0x88);
+ val |= 0xff; /* 256K */
+ rpci_write_byte(dev, 0x88, val);
+ val = pci_read_byte(dev, 0x8c);
+ val |= 0xff; /* 1M */
+ rpci_write_byte(dev, 0x8c, val);
+ wordval = pci_read_word(dev, 0x90);
+ wordval |= 0x7fff; /* 16M */
+ rpci_write_word(dev, 0x90, wordval);
+
+ if (enable_flash_nvidia_common(dev, name))
+ return ERROR_NONFATAL;
+ else
+ return 0;
+}
+
+/*
+ * The MCP6x/MCP7x code is based on cleanroom reverse engineering.
+ * It is assumed that LPC chips need the MCP55 code and SPI chips need the
+ * code provided in enable_flash_mcp6x_7x_common.
+ */
+static int enable_flash_mcp6x_7x(struct pci_dev *dev, const char *name)
+{
+ int ret = 0, want_spi = 0;
+ uint8_t val;
+
+ /* dev is the ISA bridge. No idea what the stuff below does. */
+ val = pci_read_byte(dev, 0x8a);
+ msg_pdbg("ISA/LPC bridge reg 0x8a contents: 0x%02x, bit 6 is %i, bit 5 "
+ "is %i\n", val, (val >> 6) & 0x1, (val >> 5) & 0x1);
+
+ switch ((val >> 5) & 0x3) {
+ case 0x0:
+ ret = enable_flash_mcp55(dev, name);
+ internal_buses_supported = BUS_LPC;
+ msg_pdbg("Flash bus type is LPC\n");
+ break;
+ case 0x2:
+ want_spi = 1;
+ /* SPI is added in mcp6x_spi_init if it works.
+ * Do we really want to disable LPC in this case?
+ */
+ internal_buses_supported = BUS_NONE;
+ msg_pdbg("Flash bus type is SPI\n");
+ break;
+ default:
+ /* Should not happen. */
+ internal_buses_supported = BUS_NONE;
+ msg_pwarn("Flash bus type is unknown (none)\n");
+ msg_pinfo("Please send the log files created by \"flashrom -p internal -o logfile\" to \n"
+ "flashrom@flashrom.org with \"your board name: flashrom -V\" as the subject to\n"
+ "help us finish support for your chipset. Thanks.\n");
+ return ERROR_NONFATAL;
+ }
+
+ /* Force enable SPI and disable LPC? Not a good idea. */
+#if 0
+ val |= (1 << 6);
+ val &= ~(1 << 5);
+ rpci_write_byte(dev, 0x8a, val);
+#endif
+
+ if (mcp6x_spi_init(want_spi))
+ ret = 1;
+
+ return ret;
+}
+
+static int enable_flash_ht1000(struct pci_dev *dev, const char *name)
+{
+ uint8_t val;
+
+ /* Set the 4MB enable bit. */
+ val = pci_read_byte(dev, 0x41);
+ val |= 0x0e;
+ rpci_write_byte(dev, 0x41, val);
+
+ val = pci_read_byte(dev, 0x43);
+ val |= (1 << 4);
+ rpci_write_byte(dev, 0x43, val);
+
+ return 0;
+}
+
+/*
+ * Usually on the x86 architectures (and on other PC-like platforms like some
+ * Alphas or Itanium) the system flash is mapped right below 4G. On the AMD
+ * Elan SC520 only a small piece of the system flash is mapped there, but the
+ * complete flash is mapped somewhere below 1G. The position can be determined
+ * by the BOOTCS PAR register.
+ */
+static int get_flashbase_sc520(struct pci_dev *dev, const char *name)
+{
+ int i, bootcs_found = 0;
+ uint32_t parx = 0;
+ void *mmcr;
+
+ /* 1. Map MMCR */
+ mmcr = physmap("Elan SC520 MMCR", 0xfffef000, getpagesize());
+ if (mmcr == ERROR_PTR)
+ return ERROR_FATAL;
+
+ /* 2. Scan PAR0 (0x88) - PAR15 (0xc4) for
+ * BOOTCS region (PARx[31:29] = 100b)e
+ */
+ for (i = 0x88; i <= 0xc4; i += 4) {
+ parx = mmio_readl(mmcr + i);
+ if ((parx >> 29) == 4) {
+ bootcs_found = 1;
+ break; /* BOOTCS found */
+ }
+ }
+
+ /* 3. PARx[25] = 1b --> flashbase[29:16] = PARx[13:0]
+ * PARx[25] = 0b --> flashbase[29:12] = PARx[17:0]
+ */
+ if (bootcs_found) {
+ if (parx & (1 << 25)) {
+ parx &= (1 << 14) - 1; /* Mask [13:0] */
+ flashbase = parx << 16;
+ } else {
+ parx &= (1 << 18) - 1; /* Mask [17:0] */
+ flashbase = parx << 12;
+ }
+ } else {
+ msg_pinfo("AMD Elan SC520 detected, but no BOOTCS. "
+ "Assuming flash at 4G.\n");
+ }
+
+ /* 4. Clean up */
+ physunmap(mmcr, getpagesize());
+ return 0;
+}
+
+#endif
+
+/* Please keep this list numerically sorted by vendor/device ID. */
+const struct penable chipset_enables[] = {
+#if defined(__i386__) || defined(__x86_64__)
+ {0x1002, 0x4377, OK, "ATI", "SB400", enable_flash_sb400},
+ {0x1002, 0x438d, OK, "AMD", "SB600", enable_flash_sb600},
+ {0x1002, 0x439d, OK, "AMD", "SB7x0/SB8x0/SB9x0", enable_flash_sb600},
+ {0x100b, 0x0510, NT, "AMD", "SC1100", enable_flash_sc1100},
+ {0x1022, 0x2080, OK, "AMD", "CS5536", enable_flash_cs5536},
+ {0x1022, 0x2090, OK, "AMD", "CS5536", enable_flash_cs5536},
+ {0x1022, 0x3000, OK, "AMD", "Elan SC520", get_flashbase_sc520},
+ {0x1022, 0x7440, OK, "AMD", "AMD-768", enable_flash_amd_768_8111},
+ {0x1022, 0x7468, OK, "AMD", "AMD-8111", enable_flash_amd_768_8111},
+ {0x1022, 0x780e, OK, "AMD", "FCH", enable_flash_sb600},
+ {0x1039, 0x0406, NT, "SiS", "501/5101/5501", enable_flash_sis501},
+ {0x1039, 0x0496, NT, "SiS", "85C496+497", enable_flash_sis85c496},
+ {0x1039, 0x0530, OK, "SiS", "530", enable_flash_sis530},
+ {0x1039, 0x0540, NT, "SiS", "540", enable_flash_sis540},
+ {0x1039, 0x0620, NT, "SiS", "620", enable_flash_sis530},
+ {0x1039, 0x0630, NT, "SiS", "630", enable_flash_sis540},
+ {0x1039, 0x0635, NT, "SiS", "635", enable_flash_sis540},
+ {0x1039, 0x0640, NT, "SiS", "640", enable_flash_sis540},
+ {0x1039, 0x0645, NT, "SiS", "645", enable_flash_sis540},
+ {0x1039, 0x0646, OK, "SiS", "645DX", enable_flash_sis540},
+ {0x1039, 0x0648, OK, "SiS", "648", enable_flash_sis540},
+ {0x1039, 0x0650, OK, "SiS", "650", enable_flash_sis540},
+ {0x1039, 0x0651, OK, "SiS", "651", enable_flash_sis540},
+ {0x1039, 0x0655, NT, "SiS", "655", enable_flash_sis540},
+ {0x1039, 0x0661, OK, "SiS", "661", enable_flash_sis540},
+ {0x1039, 0x0730, OK, "SiS", "730", enable_flash_sis540},
+ {0x1039, 0x0733, NT, "SiS", "733", enable_flash_sis540},
+ {0x1039, 0x0735, OK, "SiS", "735", enable_flash_sis540},
+ {0x1039, 0x0740, NT, "SiS", "740", enable_flash_sis540},
+ {0x1039, 0x0741, OK, "SiS", "741", enable_flash_sis540},
+ {0x1039, 0x0745, OK, "SiS", "745", enable_flash_sis540},
+ {0x1039, 0x0746, NT, "SiS", "746", enable_flash_sis540},
+ {0x1039, 0x0748, NT, "SiS", "748", enable_flash_sis540},
+ {0x1039, 0x0755, OK, "SiS", "755", enable_flash_sis540},
+ {0x1039, 0x5511, NT, "SiS", "5511", enable_flash_sis5511},
+ {0x1039, 0x5571, NT, "SiS", "5571", enable_flash_sis530},
+ {0x1039, 0x5591, NT, "SiS", "5591/5592", enable_flash_sis530},
+ {0x1039, 0x5596, NT, "SiS", "5596", enable_flash_sis5511},
+ {0x1039, 0x5597, NT, "SiS", "5597/5598/5581/5120", enable_flash_sis530},
+ {0x1039, 0x5600, NT, "SiS", "600", enable_flash_sis530},
+ {0x1078, 0x0100, OK, "AMD", "CS5530(A)", enable_flash_cs5530},
+ {0x10b9, 0x1533, OK, "ALi", "M1533", enable_flash_ali_m1533},
+ {0x10de, 0x0030, OK, "NVIDIA", "nForce4/MCP4", enable_flash_nvidia_nforce2},
+ {0x10de, 0x0050, OK, "NVIDIA", "CK804", enable_flash_ck804}, /* LPC */
+ {0x10de, 0x0051, OK, "NVIDIA", "CK804", enable_flash_ck804}, /* Pro */
+ {0x10de, 0x0060, OK, "NVIDIA", "NForce2", enable_flash_nvidia_nforce2},
+ {0x10de, 0x00e0, OK, "NVIDIA", "NForce3", enable_flash_nvidia_nforce2},
+ /* Slave, should not be here, to fix known bug for A01. */
+ {0x10de, 0x00d3, OK, "NVIDIA", "CK804", enable_flash_ck804},
+ {0x10de, 0x0260, OK, "NVIDIA", "MCP51", enable_flash_ck804},
+ {0x10de, 0x0261, OK, "NVIDIA", "MCP51", enable_flash_ck804},
+ {0x10de, 0x0262, NT, "NVIDIA", "MCP51", enable_flash_ck804},
+ {0x10de, 0x0263, NT, "NVIDIA", "MCP51", enable_flash_ck804},
+ {0x10de, 0x0360, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* M57SLI*/
+ /* 10de:0361 is present in Tyan S2915 OEM systems, but not connected to
+ * the flash chip. Instead, 10de:0364 is connected to the flash chip.
+ * Until we have PCI device class matching or some fallback mechanism,
+ * this is needed to get flashrom working on Tyan S2915 and maybe other
+ * dual-MCP55 boards.
+ */
+#if 0
+ {0x10de, 0x0361, NT, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+#endif
+ {0x10de, 0x0362, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+ {0x10de, 0x0363, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+ {0x10de, 0x0364, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+ {0x10de, 0x0365, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+ {0x10de, 0x0366, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* LPC */
+ {0x10de, 0x0367, OK, "NVIDIA", "MCP55", enable_flash_mcp55}, /* Pro */
+ {0x10de, 0x03e0, OK, "NVIDIA", "MCP61", enable_flash_mcp6x_7x},
+ {0x10de, 0x03e1, OK, "NVIDIA", "MCP61", enable_flash_mcp6x_7x},
+ {0x10de, 0x03e3, NT, "NVIDIA", "MCP61", enable_flash_mcp6x_7x},
+ {0x10de, 0x0440, NT, "NVIDIA", "MCP65", enable_flash_mcp6x_7x},
+ {0x10de, 0x0441, NT, "NVIDIA", "MCP65", enable_flash_mcp6x_7x},
+ {0x10de, 0x0442, NT, "NVIDIA", "MCP65", enable_flash_mcp6x_7x},
+ {0x10de, 0x0443, NT, "NVIDIA", "MCP65", enable_flash_mcp6x_7x},
+ {0x10de, 0x0548, OK, "NVIDIA", "MCP67", enable_flash_mcp6x_7x},
+ {0x10de, 0x075c, OK, "NVIDIA", "MCP78S", enable_flash_mcp6x_7x},
+ {0x10de, 0x075d, OK, "NVIDIA", "MCP78S", enable_flash_mcp6x_7x},
+ {0x10de, 0x07d7, OK, "NVIDIA", "MCP73", enable_flash_mcp6x_7x},
+ {0x10de, 0x0aac, OK, "NVIDIA", "MCP79", enable_flash_mcp6x_7x},
+ {0x10de, 0x0aad, NT, "NVIDIA", "MCP79", enable_flash_mcp6x_7x},
+ {0x10de, 0x0aae, NT, "NVIDIA", "MCP79", enable_flash_mcp6x_7x},
+ {0x10de, 0x0aaf, NT, "NVIDIA", "MCP79", enable_flash_mcp6x_7x},
+ {0x10de, 0x0d80, NT, "NVIDIA", "MCP89", enable_flash_mcp6x_7x},
+ /* VIA northbridges */
+ {0x1106, 0x0585, NT, "VIA", "VT82C585VPX", via_no_byte_merge},
+ {0x1106, 0x0595, NT, "VIA", "VT82C595", via_no_byte_merge},
+ {0x1106, 0x0597, NT, "VIA", "VT82C597", via_no_byte_merge},
+ {0x1106, 0x0601, NT, "VIA", "VT8601/VT8601A", via_no_byte_merge},
+ {0x1106, 0x0691, OK, "VIA", "VT82C69x", via_no_byte_merge},
+ {0x1106, 0x8601, NT, "VIA", "VT8601T", via_no_byte_merge},
+ /* VIA southbridges */
+ {0x1106, 0x0586, OK, "VIA", "VT82C586A/B", enable_flash_vt82c586},
+ {0x1106, 0x0596, OK, "VIA", "VT82C596", enable_flash_vt82c596},
+ {0x1106, 0x0686, OK, "VIA", "VT82C686A/B", enable_flash_vt82c596},
+ {0x1106, 0x3074, OK, "VIA", "VT8233", enable_flash_vt823x},
+ {0x1106, 0x3147, OK, "VIA", "VT8233A", enable_flash_vt823x},
+ {0x1106, 0x3177, OK, "VIA", "VT8235", enable_flash_vt823x},
+ {0x1106, 0x3227, OK, "VIA", "VT8237(R)", enable_flash_vt823x},
+ {0x1106, 0x3287, OK, "VIA", "VT8251", enable_flash_vt823x},
+ {0x1106, 0x3337, OK, "VIA", "VT8237A", enable_flash_vt823x},
+ {0x1106, 0x3372, OK, "VIA", "VT8237S", enable_flash_vt8237s_spi},
+ {0x1106, 0x8231, NT, "VIA", "VT8231", enable_flash_vt823x},
+ {0x1106, 0x8324, OK, "VIA", "CX700", enable_flash_vt823x},
+ {0x1106, 0x8353, NT, "VIA", "VX800/VX820", enable_flash_vt_vx},
+ {0x1106, 0x8409, NT, "VIA", "VX855/VX875", enable_flash_vt_vx},
+ {0x1106, 0x8410, NT, "VIA", "VX900", enable_flash_vt_vx},
+ {0x1166, 0x0200, OK, "Broadcom", "OSB4", enable_flash_osb4},
+ {0x1166, 0x0205, OK, "Broadcom", "HT-1000", enable_flash_ht1000},
+ {0x17f3, 0x6030, OK, "RDC", "R8610/R3210", enable_flash_rdc_r8610},
+ {0x8086, 0x0c60, NT, "Intel", "S12x0", enable_flash_s12x0},
+ {0x8086, 0x0f1c, OK, "Intel", "Bay Trail", enable_flash_silvermont},
+ {0x8086, 0x0f1d, NT, "Intel", "Bay Trail", enable_flash_silvermont},
+ {0x8086, 0x0f1e, NT, "Intel", "Bay Trail", enable_flash_silvermont},
+ {0x8086, 0x0f1f, NT, "Intel", "Bay Trail", enable_flash_silvermont},
+ {0x8086, 0x122e, OK, "Intel", "PIIX", enable_flash_piix4},
+ {0x8086, 0x1234, NT, "Intel", "MPIIX", enable_flash_piix4},
+ {0x8086, 0x1c44, DEP, "Intel", "Z68", enable_flash_pch6},
+ {0x8086, 0x1c46, DEP, "Intel", "P67", enable_flash_pch6},
+ {0x8086, 0x1c47, NT, "Intel", "UM67", enable_flash_pch6},
+ {0x8086, 0x1c49, NT, "Intel", "HM65", enable_flash_pch6},
+ {0x8086, 0x1c4a, DEP, "Intel", "H67", enable_flash_pch6},
+ {0x8086, 0x1c4b, NT, "Intel", "HM67", enable_flash_pch6},
+ {0x8086, 0x1c4c, NT, "Intel", "Q65", enable_flash_pch6},
+ {0x8086, 0x1c4d, NT, "Intel", "QS67", enable_flash_pch6},
+ {0x8086, 0x1c4e, NT, "Intel", "Q67", enable_flash_pch6},
+ {0x8086, 0x1c4f, DEP, "Intel", "QM67", enable_flash_pch6},
+ {0x8086, 0x1c50, NT, "Intel", "B65", enable_flash_pch6},
+ {0x8086, 0x1c52, NT, "Intel", "C202", enable_flash_pch6},
+ {0x8086, 0x1c54, DEP, "Intel", "C204", enable_flash_pch6},
+ {0x8086, 0x1c56, NT, "Intel", "C206", enable_flash_pch6},
+ {0x8086, 0x1c5c, DEP, "Intel", "H61", enable_flash_pch6},
+ {0x8086, 0x1d40, DEP, "Intel", "C60x/X79", enable_flash_pch6},
+ {0x8086, 0x1d41, DEP, "Intel", "C60x/X79", enable_flash_pch6},
+ {0x8086, 0x1e44, DEP, "Intel", "Z77", enable_flash_pch7},
+ {0x8086, 0x1e46, NT, "Intel", "Z75", enable_flash_pch7},
+ {0x8086, 0x1e47, NT, "Intel", "Q77", enable_flash_pch7},
+ {0x8086, 0x1e48, NT, "Intel", "Q75", enable_flash_pch7},
+ {0x8086, 0x1e49, DEP, "Intel", "B75", enable_flash_pch7},
+ {0x8086, 0x1e4a, DEP, "Intel", "H77", enable_flash_pch7},
+ {0x8086, 0x1e53, NT, "Intel", "C216", enable_flash_pch7},
+ {0x8086, 0x1e55, DEP, "Intel", "QM77", enable_flash_pch7},
+ {0x8086, 0x1e56, NT, "Intel", "QS77", enable_flash_pch7},
+ {0x8086, 0x1e57, DEP, "Intel", "HM77", enable_flash_pch7},
+ {0x8086, 0x1e58, NT, "Intel", "UM77", enable_flash_pch7},
+ {0x8086, 0x1e59, NT, "Intel", "HM76", enable_flash_pch7},
+ {0x8086, 0x1e5d, NT, "Intel", "HM75", enable_flash_pch7},
+ {0x8086, 0x1e5e, NT, "Intel", "HM70", enable_flash_pch7},
+ {0x8086, 0x1e5f, DEP, "Intel", "NM70", enable_flash_pch7},
+ {0x8086, 0x1f38, DEP, "Intel", "Avoton/Rangeley", enable_flash_silvermont},
+ {0x8086, 0x1f39, NT, "Intel", "Avoton/Rangeley", enable_flash_silvermont},
+ {0x8086, 0x1f3a, NT, "Intel", "Avoton/Rangeley", enable_flash_silvermont},
+ {0x8086, 0x1f3b, NT, "Intel", "Avoton/Rangeley", enable_flash_silvermont},
+ {0x8086, 0x229c, NT, "Intel", "Braswell", enable_flash_silvermont},
+ {0x8086, 0x2310, NT, "Intel", "DH89xxCC (Cave Creek)", enable_flash_pch7},
+ {0x8086, 0x2390, NT, "Intel", "Coleto Creek", enable_flash_pch7},
+ {0x8086, 0x2410, OK, "Intel", "ICH", enable_flash_ich0},
+ {0x8086, 0x2420, OK, "Intel", "ICH0", enable_flash_ich0},
+ {0x8086, 0x2440, OK, "Intel", "ICH2", enable_flash_ich2345},
+ {0x8086, 0x244c, OK, "Intel", "ICH2-M", enable_flash_ich2345},
+ {0x8086, 0x2450, NT, "Intel", "C-ICH", enable_flash_ich2345},
+ {0x8086, 0x2480, OK, "Intel", "ICH3-S", enable_flash_ich2345},
+ {0x8086, 0x248c, OK, "Intel", "ICH3-M", enable_flash_ich2345},
+ {0x8086, 0x24c0, OK, "Intel", "ICH4/ICH4-L", enable_flash_ich2345},
+ {0x8086, 0x24cc, OK, "Intel", "ICH4-M", enable_flash_ich2345},
+ {0x8086, 0x24d0, OK, "Intel", "ICH5/ICH5R", enable_flash_ich2345},
+ {0x8086, 0x25a1, OK, "Intel", "6300ESB", enable_flash_ich2345},
+ {0x8086, 0x2640, OK, "Intel", "ICH6/ICH6R", enable_flash_ich6},
+ {0x8086, 0x2641, OK, "Intel", "ICH6-M", enable_flash_ich6},
+ {0x8086, 0x2642, NT, "Intel", "ICH6W/ICH6RW", enable_flash_ich6},
+ {0x8086, 0x2670, OK, "Intel", "631xESB/632xESB/3100", enable_flash_ich6},
+ {0x8086, 0x27b0, OK, "Intel", "ICH7DH", enable_flash_ich7},
+ {0x8086, 0x27b8, OK, "Intel", "ICH7/ICH7R", enable_flash_ich7},
+ {0x8086, 0x27b9, OK, "Intel", "ICH7M", enable_flash_ich7},
+ {0x8086, 0x27bc, OK, "Intel", "NM10", enable_flash_ich7},
+ {0x8086, 0x27bd, OK, "Intel", "ICH7MDH", enable_flash_ich7},
+ {0x8086, 0x2810, DEP, "Intel", "ICH8/ICH8R", enable_flash_ich8},
+ {0x8086, 0x2811, DEP, "Intel", "ICH8M-E", enable_flash_ich8},
+ {0x8086, 0x2812, DEP, "Intel", "ICH8DH", enable_flash_ich8},
+ {0x8086, 0x2814, DEP, "Intel", "ICH8DO", enable_flash_ich8},
+ {0x8086, 0x2815, DEP, "Intel", "ICH8M", enable_flash_ich8},
+ {0x8086, 0x2910, DEP, "Intel", "ICH9 Eng. Sample", enable_flash_ich9},
+ {0x8086, 0x2912, DEP, "Intel", "ICH9DH", enable_flash_ich9},
+ {0x8086, 0x2914, DEP, "Intel", "ICH9DO", enable_flash_ich9},
+ {0x8086, 0x2916, DEP, "Intel", "ICH9R", enable_flash_ich9},
+ {0x8086, 0x2917, DEP, "Intel", "ICH9M-E", enable_flash_ich9},
+ {0x8086, 0x2918, DEP, "Intel", "ICH9", enable_flash_ich9},
+ {0x8086, 0x2919, DEP, "Intel", "ICH9M", enable_flash_ich9},
+ {0x8086, 0x3a10, NT, "Intel", "ICH10R Eng. Sample", enable_flash_ich10},
+ {0x8086, 0x3a14, DEP, "Intel", "ICH10DO", enable_flash_ich10},
+ {0x8086, 0x3a16, DEP, "Intel", "ICH10R", enable_flash_ich10},
+ {0x8086, 0x3a18, DEP, "Intel", "ICH10", enable_flash_ich10},
+ {0x8086, 0x3a1a, DEP, "Intel", "ICH10D", enable_flash_ich10},
+ {0x8086, 0x3a1e, NT, "Intel", "ICH10 Eng. Sample", enable_flash_ich10},
+ {0x8086, 0x3b00, NT, "Intel", "3400 Desktop", enable_flash_pch5},
+ {0x8086, 0x3b01, NT, "Intel", "3400 Mobile", enable_flash_pch5},
+ {0x8086, 0x3b02, NT, "Intel", "P55", enable_flash_pch5},
+ {0x8086, 0x3b03, NT, "Intel", "PM55", enable_flash_pch5},
+ {0x8086, 0x3b06, DEP, "Intel", "H55", enable_flash_pch5},
+ {0x8086, 0x3b07, DEP, "Intel", "QM57", enable_flash_pch5},
+ {0x8086, 0x3b08, NT, "Intel", "H57", enable_flash_pch5},
+ {0x8086, 0x3b09, NT, "Intel", "HM55", enable_flash_pch5},
+ {0x8086, 0x3b0a, NT, "Intel", "Q57", enable_flash_pch5},
+ {0x8086, 0x3b0b, NT, "Intel", "HM57", enable_flash_pch5},
+ {0x8086, 0x3b0d, NT, "Intel", "3400 Mobile SFF", enable_flash_pch5},
+ {0x8086, 0x3b0e, NT, "Intel", "B55", enable_flash_pch5},
+ {0x8086, 0x3b0f, DEP, "Intel", "QS57", enable_flash_pch5},
+ {0x8086, 0x3b12, NT, "Intel", "3400", enable_flash_pch5},
+ {0x8086, 0x3b14, DEP, "Intel", "3420", enable_flash_pch5},
+ {0x8086, 0x3b16, NT, "Intel", "3450", enable_flash_pch5},
+ {0x8086, 0x3b1e, NT, "Intel", "B55", enable_flash_pch5},
+ {0x8086, 0x5031, OK, "Intel", "EP80579", enable_flash_ich7},
+ {0x8086, 0x7000, OK, "Intel", "PIIX3", enable_flash_piix4},
+ {0x8086, 0x7110, OK, "Intel", "PIIX4/4E/4M", enable_flash_piix4},
+ {0x8086, 0x7198, OK, "Intel", "440MX", enable_flash_piix4},
+ {0x8086, 0x8119, OK, "Intel", "SCH Poulsbo", enable_flash_poulsbo},
+ {0x8086, 0x8186, OK, "Intel", "Atom E6xx(T) (Tunnel Creek)", enable_flash_tunnelcreek},
+ {0x8086, 0x8c40, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c41, NT, "Intel", "Lynx Point Mobile Eng. Sample", enable_flash_pch8},
+ {0x8086, 0x8c42, NT, "Intel", "Lynx Point Desktop Eng. Sample",enable_flash_pch8},
+ {0x8086, 0x8c43, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c44, DEP, "Intel", "Z87", enable_flash_pch8},
+ {0x8086, 0x8c45, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c46, NT, "Intel", "Z85", enable_flash_pch8},
+ {0x8086, 0x8c47, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c48, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c49, NT, "Intel", "HM86", enable_flash_pch8},
+ {0x8086, 0x8c4a, DEP, "Intel", "H87", enable_flash_pch8},
+ {0x8086, 0x8c4b, DEP, "Intel", "HM87", enable_flash_pch8},
+ {0x8086, 0x8c4c, NT, "Intel", "Q85", enable_flash_pch8},
+ {0x8086, 0x8c4d, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c4e, NT, "Intel", "Q87", enable_flash_pch8},
+ {0x8086, 0x8c4f, NT, "Intel", "QM87", enable_flash_pch8},
+ {0x8086, 0x8c50, DEP, "Intel", "B85", enable_flash_pch8},
+ {0x8086, 0x8c51, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c52, NT, "Intel", "C222", enable_flash_pch8},
+ {0x8086, 0x8c53, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c54, NT, "Intel", "C224", enable_flash_pch8},
+ {0x8086, 0x8c55, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c56, NT, "Intel", "C226", enable_flash_pch8},
+ {0x8086, 0x8c57, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c58, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c59, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c5a, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c5b, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c5c, NT, "Intel", "H81", enable_flash_pch8},
+ {0x8086, 0x8c5d, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c5e, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8c5f, NT, "Intel", "Lynx Point", enable_flash_pch8},
+ {0x8086, 0x8cc1, NT, "Intel", "9 Series", enable_flash_pch9},
+ {0x8086, 0x8cc2, NT, "Intel", "9 Series Engineering Sample", enable_flash_pch9},
+ {0x8086, 0x8cc3, NT, "Intel", "9 Series", enable_flash_pch9},
+ {0x8086, 0x8cc4, NT, "Intel", "Z97", enable_flash_pch9},
+ {0x8086, 0x8cc6, NT, "Intel", "H97", enable_flash_pch9},
+ {0x8086, 0x8d40, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d41, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d42, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d43, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d44, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d45, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d46, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d47, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d48, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d49, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4a, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4b, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4c, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4d, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4e, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d4f, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d50, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d51, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d52, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d53, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d54, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d55, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d56, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d57, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d58, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d59, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5a, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5b, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5c, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5d, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5e, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x8d5f, NT, "Intel", "C610/X99 (Wellsburg)", enable_flash_pch8_wb},
+ {0x8086, 0x9c41, NT, "Intel", "Lynx Point LP Eng. Sample", enable_flash_pch8_lp},
+ {0x8086, 0x9c43, NT, "Intel", "Lynx Point LP Premium", enable_flash_pch8_lp},
+ {0x8086, 0x9c45, NT, "Intel", "Lynx Point LP Mainstream", enable_flash_pch8_lp},
+ {0x8086, 0x9c47, NT, "Intel", "Lynx Point LP Value", enable_flash_pch8_lp},
+ {0x8086, 0x9cc1, NT, "Intel", "Haswell U Sample", enable_flash_pch9},
+ {0x8086, 0x9cc2, NT, "Intel", "Broadwell U Sample", enable_flash_pch9},
+ {0x8086, 0x9cc3, NT, "Intel", "Broadwell U Premium", enable_flash_pch9},
+ {0x8086, 0x9cc5, NT, "Intel", "Broadwell U Base", enable_flash_pch9},
+ {0x8086, 0x9cc6, NT, "Intel", "Broadwell Y Sample", enable_flash_pch9},
+ {0x8086, 0x9cc7, NT, "Intel", "Broadwell Y Premium", enable_flash_pch9},
+ {0x8086, 0x9cc9, NT, "Intel", "Broadwell Y Base", enable_flash_pch9},
+ {0x8086, 0x9ccb, NT, "Intel", "Broadwell H", enable_flash_pch9},
+ {0x8086, 0x9d41, BAD, "Intel", "Sunrise Point (Skylake LP Sample)", NULL},
+ {0x8086, 0x9d43, BAD, "Intel", "Sunrise Point (Skylake-U Base)", NULL},
+ {0x8086, 0x9d48, BAD, "Intel", "Sunrise Point (Skylake-U Premium)", NULL},
+ {0x8086, 0x9d46, BAD, "Intel", "Sunrise Point (Skylake-Y Premium)", NULL},
+#endif
+ {0},
+};
+
+int chipset_flash_enable(void)
+{
+ struct pci_dev *dev = NULL;
+ int ret = -2; /* Nothing! */
+ int i;
+
+ /* Now let's try to find the chipset we have... */
+ for (i = 0; chipset_enables[i].vendor_name != NULL; i++) {
+ dev = pci_dev_find(chipset_enables[i].vendor_id,
+ chipset_enables[i].device_id);
+ if (!dev)
+ continue;
+ if (ret != -2) {
+ msg_pwarn("Warning: unexpected second chipset match: "
+ "\"%s %s\"\n"
+ "ignoring, please report lspci and board URL "
+ "to flashrom@flashrom.org\n"
+ "with \'CHIPSET: your board name\' in the "
+ "subject line.\n",
+ chipset_enables[i].vendor_name,
+ chipset_enables[i].device_name);
+ continue;
+ }
+ msg_pinfo("Found chipset \"%s %s\"",
+ chipset_enables[i].vendor_name,
+ chipset_enables[i].device_name);
+ msg_pdbg(" with PCI ID %04x:%04x",
+ chipset_enables[i].vendor_id,
+ chipset_enables[i].device_id);
+ msg_pinfo(".\n");
+
+ if (chipset_enables[i].status == BAD) {
+ msg_perr("ERROR: This chipset is not supported yet.\n");
+ return ERROR_FATAL;
+ }
+ if (chipset_enables[i].status == NT) {
+ msg_pinfo("This chipset is marked as untested. If "
+ "you are using an up-to-date version\nof "
+ "flashrom *and* were (not) able to "
+ "successfully update your firmware with it,\n"
+ "then please email a report to "
+ "flashrom@flashrom.org including a verbose "
+ "(-V) log.\nThank you!\n");
+ }
+ msg_pinfo("Enabling flash write... ");
+ ret = chipset_enables[i].doit(dev, chipset_enables[i].device_name);
+ if (ret == NOT_DONE_YET) {
+ ret = -2;
+ msg_pinfo("OK - searching further chips.\n");
+ } else if (ret < 0)
+ msg_pinfo("FAILED!\n");
+ else if (ret == 0)
+ msg_pinfo("OK.\n");
+ else if (ret == ERROR_NONFATAL)
+ msg_pinfo("PROBLEMS, continuing anyway\n");
+ if (ret == ERROR_FATAL) {
+ msg_perr("FATAL ERROR!\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
diff --git a/cli_classic.c b/cli_classic.c
new file mode 100644
index 0000000..a2c2014
--- /dev/null
+++ b/cli_classic.c
@@ -0,0 +1,566 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009,2010 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "programmer.h"
+
+static void cli_classic_usage(const char *name)
+{
+ printf("Please note that the command line interface for flashrom has changed between\n"
+ "0.9.5 and 0.9.6 and will change again before flashrom 1.0.\n\n");
+
+ printf("Usage: %s [-h|-R|-L|"
+#if CONFIG_PRINT_WIKI == 1
+ "-z|"
+#endif
+ "-p <programmername>[:<parameters>] [-c <chipname>]\n"
+ "[-E|(-r|-w|-v) <file>] [-l <layoutfile> [-i <imagename>]...] [-n] [-f]]\n"
+ "[-V[V[V]]] [-o <logfile>]\n\n", name);
+
+ printf(" -h | --help print this help text\n"
+ " -R | --version print version (release)\n"
+ " -r | --read <file> read flash and save to <file>\n"
+ " -w | --write <file> write <file> to flash\n"
+ " -v | --verify <file> verify flash against <file>\n"
+ " -E | --erase erase flash memory\n"
+ " -V | --verbose more verbose output\n"
+ " -c | --chip <chipname> probe only for specified flash chip\n"
+ " -f | --force force specific operations (see man page)\n"
+ " -n | --noverify don't auto-verify\n"
+ " -l | --layout <layoutfile> read ROM layout from <layoutfile>\n"
+ " -i | --image <name> only flash image <name> from flash layout\n"
+ " -o | --output <logfile> log output to <logfile>\n"
+ " -L | --list-supported print supported devices\n"
+#if CONFIG_PRINT_WIKI == 1
+ " -z | --list-supported-wiki print supported devices in wiki syntax\n"
+#endif
+ " -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
+ list_programmers_linebreak(4, 80, 0);
+ printf(".\n\nYou can specify one of -h, -R, -L, "
+#if CONFIG_PRINT_WIKI == 1
+ "-z, "
+#endif
+ "-E, -r, -w, -v or no operation.\n"
+ "If no operation is specified, flashrom will only probe for flash chips.\n");
+}
+
+static void cli_classic_abort_usage(void)
+{
+ printf("Please run \"flashrom --help\" for usage info.\n");
+ exit(1);
+}
+
+static int check_filename(char *filename, char *type)
+{
+ if (!filename || (filename[0] == '\0')) {
+ fprintf(stderr, "Error: No %s file specified.\n", type);
+ return 1;
+ }
+ /* Not an error, but maybe the user intended to specify a CLI option instead of a file name. */
+ if (filename[0] == '-')
+ fprintf(stderr, "Warning: Supplied %s file name starts with -\n", type);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ const struct flashchip *chip = NULL;
+ /* Probe for up to eight flash chips. */
+ struct flashctx flashes[8] = {{0}};
+ struct flashctx *fill_flash;
+ const char *name;
+ int namelen, opt, i, j;
+ int startchip = -1, chipcount = 0, option_index = 0, force = 0;
+#if CONFIG_PRINT_WIKI == 1
+ int list_supported_wiki = 0;
+#endif
+ int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
+ int dont_verify_it = 0, list_supported = 0, operation_specified = 0;
+ enum programmer prog = PROGRAMMER_INVALID;
+ int ret = 0;
+
+ static const char optstring[] = "r:Rw:v:nVEfc:l:i:p:Lzho:";
+ static const struct option long_options[] = {
+ {"read", 1, NULL, 'r'},
+ {"write", 1, NULL, 'w'},
+ {"erase", 0, NULL, 'E'},
+ {"verify", 1, NULL, 'v'},
+ {"noverify", 0, NULL, 'n'},
+ {"chip", 1, NULL, 'c'},
+ {"verbose", 0, NULL, 'V'},
+ {"force", 0, NULL, 'f'},
+ {"layout", 1, NULL, 'l'},
+ {"image", 1, NULL, 'i'},
+ {"list-supported", 0, NULL, 'L'},
+ {"list-supported-wiki", 0, NULL, 'z'},
+ {"programmer", 1, NULL, 'p'},
+ {"help", 0, NULL, 'h'},
+ {"version", 0, NULL, 'R'},
+ {"output", 1, NULL, 'o'},
+ {NULL, 0, NULL, 0},
+ };
+
+ char *filename = NULL;
+ char *layoutfile = NULL;
+#ifndef STANDALONE
+ char *logfile = NULL;
+#endif /* !STANDALONE */
+ char *tempstr = NULL;
+ char *pparam = NULL;
+
+ print_version();
+ print_banner();
+
+ if (selfcheck())
+ exit(1);
+
+ setbuf(stdout, NULL);
+ /* FIXME: Delay all operation_specified checks until after command
+ * line parsing to allow --help overriding everything else.
+ */
+ while ((opt = getopt_long(argc, argv, optstring,
+ long_options, &option_index)) != EOF) {
+ switch (opt) {
+ case 'r':
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ filename = strdup(optarg);
+ read_it = 1;
+ break;
+ case 'w':
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ filename = strdup(optarg);
+ write_it = 1;
+ break;
+ case 'v':
+ //FIXME: gracefully handle superfluous -v
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ if (dont_verify_it) {
+ fprintf(stderr, "--verify and --noverify are mutually exclusive. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ filename = strdup(optarg);
+ verify_it = 1;
+ break;
+ case 'n':
+ if (verify_it) {
+ fprintf(stderr, "--verify and --noverify are mutually exclusive. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ dont_verify_it = 1;
+ break;
+ case 'c':
+ chip_to_probe = strdup(optarg);
+ break;
+ case 'V':
+ verbose_screen++;
+ if (verbose_screen > MSG_DEBUG2)
+ verbose_logfile = verbose_screen;
+ break;
+ case 'E':
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ erase_it = 1;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'l':
+ if (layoutfile) {
+ fprintf(stderr, "Error: --layout specified "
+ "more than once. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ layoutfile = strdup(optarg);
+ break;
+ case 'i':
+ tempstr = strdup(optarg);
+ if (register_include_arg(tempstr)) {
+ free(tempstr);
+ cli_classic_abort_usage();
+ }
+ break;
+ case 'L':
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ list_supported = 1;
+ break;
+ case 'z':
+#if CONFIG_PRINT_WIKI == 1
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ list_supported_wiki = 1;
+#else
+ fprintf(stderr, "Error: Wiki output was not compiled "
+ "in. Aborting.\n");
+ cli_classic_abort_usage();
+#endif
+ break;
+ case 'p':
+ if (prog != PROGRAMMER_INVALID) {
+ fprintf(stderr, "Error: --programmer specified "
+ "more than once. You can separate "
+ "multiple\nparameters for a programmer "
+ "with \",\". Please see the man page "
+ "for details.\n");
+ cli_classic_abort_usage();
+ }
+ for (prog = 0; prog < PROGRAMMER_INVALID; prog++) {
+ name = programmer_table[prog].name;
+ namelen = strlen(name);
+ if (strncmp(optarg, name, namelen) == 0) {
+ switch (optarg[namelen]) {
+ case ':':
+ pparam = strdup(optarg + namelen + 1);
+ if (!strlen(pparam)) {
+ free(pparam);
+ pparam = NULL;
+ }
+ break;
+ case '\0':
+ break;
+ default:
+ /* The continue refers to the
+ * for loop. It is here to be
+ * able to differentiate between
+ * foo and foobar.
+ */
+ continue;
+ }
+ break;
+ }
+ }
+ if (prog == PROGRAMMER_INVALID) {
+ fprintf(stderr, "Error: Unknown programmer \"%s\". Valid choices are:\n",
+ optarg);
+ list_programmers_linebreak(0, 80, 0);
+ msg_ginfo(".\n");
+ cli_classic_abort_usage();
+ }
+ break;
+ case 'R':
+ /* print_version() is always called during startup. */
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ exit(0);
+ break;
+ case 'h':
+ if (++operation_specified > 1) {
+ fprintf(stderr, "More than one operation "
+ "specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ cli_classic_usage(argv[0]);
+ exit(0);
+ break;
+ case 'o':
+#ifdef STANDALONE
+ fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n");
+ cli_classic_abort_usage();
+#else /* STANDALONE */
+ logfile = strdup(optarg);
+ if (logfile[0] == '\0') {
+ fprintf(stderr, "No log filename specified.\n");
+ cli_classic_abort_usage();
+ }
+#endif /* STANDALONE */
+ break;
+ default:
+ cli_classic_abort_usage();
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Error: Extra parameter found.\n");
+ cli_classic_abort_usage();
+ }
+
+ if ((read_it | write_it | verify_it) && check_filename(filename, "image")) {
+ cli_classic_abort_usage();
+ }
+ if (layoutfile && check_filename(layoutfile, "layout")) {
+ cli_classic_abort_usage();
+ }
+
+#ifndef STANDALONE
+ if (logfile && check_filename(logfile, "log"))
+ cli_classic_abort_usage();
+ if (logfile && open_logfile(logfile))
+ cli_classic_abort_usage();
+#endif /* !STANDALONE */
+
+#if CONFIG_PRINT_WIKI == 1
+ if (list_supported_wiki) {
+ print_supported_wiki();
+ goto out;
+ }
+#endif
+
+ if (list_supported) {
+ if (print_supported())
+ ret = 1;
+ goto out;
+ }
+
+#ifndef STANDALONE
+ start_logging();
+#endif /* !STANDALONE */
+
+ print_buildinfo();
+ msg_gdbg("Command line (%i args):", argc - 1);
+ for (i = 0; i < argc; i++) {
+ msg_gdbg(" %s", argv[i]);
+ }
+ msg_gdbg("\n");
+
+ if (layoutfile && read_romlayout(layoutfile)) {
+ ret = 1;
+ goto out;
+ }
+ if (layoutfile != NULL && !write_it) {
+ msg_gerr("Layout files are currently supported for write operations only.\n");
+ ret = 1;
+ goto out;
+ }
+
+ if (process_include_args()) {
+ ret = 1;
+ goto out;
+ }
+ /* Does a chip with the requested name exist in the flashchips array? */
+ if (chip_to_probe) {
+ for (chip = flashchips; chip && chip->name; chip++)
+ if (!strcmp(chip->name, chip_to_probe))
+ break;
+ if (!chip || !chip->name) {
+ msg_cerr("Error: Unknown chip '%s' specified.\n", chip_to_probe);
+ msg_gerr("Run flashrom -L to view the hardware supported in this flashrom version.\n");
+ ret = 1;
+ goto out;
+ }
+ /* Keep chip around for later usage in case a forced read is requested. */
+ }
+
+ if (prog == PROGRAMMER_INVALID) {
+ if (CONFIG_DEFAULT_PROGRAMMER != PROGRAMMER_INVALID) {
+ prog = CONFIG_DEFAULT_PROGRAMMER;
+ /* We need to strdup here because we free(pparam) unconditionally later. */
+ pparam = strdup(CONFIG_DEFAULT_PROGRAMMER_ARGS);
+ msg_pinfo("Using default programmer \"%s\" with arguments \"%s\".\n",
+ programmer_table[CONFIG_DEFAULT_PROGRAMMER].name, pparam);
+ } else {
+ msg_perr("Please select a programmer with the --programmer parameter.\n"
+ "Previously this was not necessary because there was a default set.\n"
+#if CONFIG_INTERNAL == 1
+ "To choose the mainboard of this computer use 'internal'. "
+#endif
+ "Valid choices are:\n");
+ list_programmers_linebreak(0, 80, 0);
+ msg_ginfo(".\n");
+ ret = 1;
+ goto out;
+ }
+ }
+
+ /* FIXME: Delay calibration should happen in programmer code. */
+ myusec_calibrate_delay();
+
+ if (programmer_init(prog, pparam)) {
+ msg_perr("Error: Programmer initialization failed.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ tempstr = flashbuses_to_text(get_buses_supported());
+ msg_pdbg("The following protocols are supported: %s.\n", tempstr);
+ free(tempstr);
+
+ for (j = 0; j < registered_master_count; j++) {
+ startchip = 0;
+ while (chipcount < ARRAY_SIZE(flashes)) {
+ startchip = probe_flash(&registered_masters[j], startchip, &flashes[chipcount], 0);
+ if (startchip == -1)
+ break;
+ chipcount++;
+ startchip++;
+ }
+ }
+
+ if (chipcount > 1) {
+ msg_cinfo("Multiple flash chip definitions match the detected chip(s): \"%s\"",
+ flashes[0].chip->name);
+ for (i = 1; i < chipcount; i++)
+ msg_cinfo(", \"%s\"", flashes[i].chip->name);
+ msg_cinfo("\nPlease specify which chip definition to use with the -c <chipname> option.\n");
+ ret = 1;
+ goto out_shutdown;
+ } else if (!chipcount) {
+ msg_cinfo("No EEPROM/flash device found.\n");
+ if (!force || !chip_to_probe) {
+ msg_cinfo("Note: flashrom can never write if the flash chip isn't found "
+ "automatically.\n");
+ }
+ if (force && read_it && chip_to_probe) {
+ struct registered_master *mst;
+ int compatible_masters = 0;
+ msg_cinfo("Force read (-f -r -c) requested, pretending the chip is there:\n");
+ /* This loop just counts compatible controllers. */
+ for (j = 0; j < registered_master_count; j++) {
+ mst = &registered_masters[j];
+ /* chip is still set from the chip_to_probe earlier in this function. */
+ if (mst->buses_supported & chip->bustype)
+ compatible_masters++;
+ }
+ if (!compatible_masters) {
+ msg_cinfo("No compatible controller found for the requested flash chip.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+ if (compatible_masters > 1)
+ msg_cinfo("More than one compatible controller found for the requested flash "
+ "chip, using the first one.\n");
+ for (j = 0; j < registered_master_count; j++) {
+ mst = &registered_masters[j];
+ startchip = probe_flash(mst, 0, &flashes[0], 1);
+ if (startchip != -1)
+ break;
+ }
+ if (startchip == -1) {
+ // FIXME: This should never happen! Ask for a bug report?
+ msg_cinfo("Probing for flash chip '%s' failed.\n", chip_to_probe);
+ ret = 1;
+ goto out_shutdown;
+ }
+ if (map_flash(&flashes[0]) != 0) {
+ free(flashes[0].chip);
+ ret = 1;
+ goto out_shutdown;
+ }
+ msg_cinfo("Please note that forced reads most likely contain garbage.\n");
+ ret = read_flash_to_file(&flashes[0], filename);
+ unmap_flash(&flashes[0]);
+ free(flashes[0].chip);
+ goto out_shutdown;
+ }
+ ret = 1;
+ goto out_shutdown;
+ } else if (!chip_to_probe) {
+ /* repeat for convenience when looking at foreign logs */
+ tempstr = flashbuses_to_text(flashes[0].chip->bustype);
+ msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n",
+ flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr);
+ free(tempstr);
+ }
+
+ fill_flash = &flashes[0];
+
+ print_chip_support_status(fill_flash->chip);
+
+ unsigned int limitexceeded = count_max_decode_exceedings(fill_flash);
+ if (limitexceeded > 0 && !force) {
+ enum chipbustype commonbuses = fill_flash->mst->buses_supported & fill_flash->chip->bustype;
+
+ /* Sometimes chip and programmer have more than one bus in common,
+ * and the limit is not exceeded on all buses. Tell the user. */
+ if ((bitcount(commonbuses) > limitexceeded)) {
+ msg_pdbg("There is at least one interface available which could support the size of\n"
+ "the selected flash chip.\n");
+ }
+ msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
+ "Use --force/-f to override at your own risk.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (!(read_it | write_it | verify_it | erase_it)) {
+ msg_ginfo("No operations were specified.\n");
+ goto out_shutdown;
+ }
+
+ /* Always verify write operations unless -n is used. */
+ if (write_it && !dont_verify_it)
+ verify_it = 1;
+
+ /* Map the selected flash chip again. */
+ if (map_flash(fill_flash) != 0) {
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ /* FIXME: We should issue an unconditional chip reset here. This can be
+ * done once we have a .reset function in struct flashchip.
+ * Give the chip time to settle.
+ */
+ programmer_delay(100000);
+ ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
+
+ unmap_flash(fill_flash);
+out_shutdown:
+ programmer_shutdown();
+out:
+ for (i = 0; i < chipcount; i++)
+ free(flashes[i].chip);
+
+ layout_cleanup();
+ free(filename);
+ free(layoutfile);
+ free(pparam);
+ /* clean up global variables */
+ free((char *)chip_to_probe); /* Silence! Freeing is not modifying contents. */
+ chip_to_probe = NULL;
+#ifndef STANDALONE
+ free(logfile);
+ ret |= close_logfile();
+#endif /* !STANDALONE */
+ return ret;
+}
diff --git a/cli_common.c b/cli_common.c
new file mode 100644
index 0000000..71cc2dd
--- /dev/null
+++ b/cli_common.c
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2011-2014 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+
+/*
+ * Return a string corresponding to the bustype parameter.
+ * Memory is obtained with malloc() and must be freed with free() by the caller.
+ */
+char *flashbuses_to_text(enum chipbustype bustype)
+{
+ char *ret = calloc(1, 1);
+ /*
+ * FIXME: Once all chipsets and flash chips have been updated, NONSPI
+ * will cease to exist and should be eliminated here as well.
+ */
+ if (bustype == BUS_NONSPI) {
+ ret = strcat_realloc(ret, "Non-SPI, ");
+ } else {
+ if (bustype & BUS_PARALLEL)
+ ret = strcat_realloc(ret, "Parallel, ");
+ if (bustype & BUS_LPC)
+ ret = strcat_realloc(ret, "LPC, ");
+ if (bustype & BUS_FWH)
+ ret = strcat_realloc(ret, "FWH, ");
+ if (bustype & BUS_SPI)
+ ret = strcat_realloc(ret, "SPI, ");
+ if (bustype & BUS_PROG)
+ ret = strcat_realloc(ret, "Programmer-specific, ");
+ if (bustype == BUS_NONE)
+ ret = strcat_realloc(ret, "None, ");
+ }
+ /* Kill last comma. */
+ ret[strlen(ret) - 2] = '\0';
+ ret = realloc(ret, strlen(ret) + 1);
+ return ret;
+}
+
+
+void print_chip_support_status(const struct flashchip *chip)
+{
+ if (chip->feature_bits & FEATURE_OTP) {
+ msg_cdbg("This chip may contain one-time programmable memory. flashrom cannot read\n"
+ "and may never be able to write it, hence it may not be able to completely\n"
+ "clone the contents of this chip (see man page for details).\n");
+ }
+
+ if ((chip->tested.erase == NA) && (chip->tested.write == NA)) {
+ msg_cdbg("This chip's main memory can not be erased/written by design.\n");
+ }
+
+ if ((chip->tested.probe == BAD) || (chip->tested.probe == NT) ||
+ (chip->tested.read == BAD) || (chip->tested.read == NT) ||
+ (chip->tested.erase == BAD) || (chip->tested.erase == NT) ||
+ (chip->tested.write == BAD) || (chip->tested.write == NT)){
+ msg_cinfo("===\n");
+ if ((chip->tested.probe == BAD) ||
+ (chip->tested.read == BAD) ||
+ (chip->tested.erase == BAD) ||
+ (chip->tested.write == BAD)) {
+ msg_cinfo("This flash part has status NOT WORKING for operations:");
+ if (chip->tested.probe == BAD)
+ msg_cinfo(" PROBE");
+ if (chip->tested.read == BAD)
+ msg_cinfo(" READ");
+ if (chip->tested.erase == BAD)
+ msg_cinfo(" ERASE");
+ if (chip->tested.write == BAD)
+ msg_cinfo(" WRITE");
+ msg_cinfo("\n");
+ }
+ if ((chip->tested.probe == NT) ||
+ (chip->tested.read == NT) ||
+ (chip->tested.erase == NT) ||
+ (chip->tested.write == NT)) {
+ msg_cinfo("This flash part has status UNTESTED for operations:");
+ if (chip->tested.probe == NT)
+ msg_cinfo(" PROBE");
+ if (chip->tested.read == NT)
+ msg_cinfo(" READ");
+ if (chip->tested.erase == NT)
+ msg_cinfo(" ERASE");
+ if (chip->tested.write == NT)
+ msg_cinfo(" WRITE");
+ msg_cinfo("\n");
+ }
+ msg_cinfo("The test status of this chip may have been updated in the latest development\n"
+ "version of flashrom. If you are running the latest development version,\n"
+ "please email a report to flashrom@flashrom.org if any of the above operations\n"
+ "work correctly for you with this flash chip. Please include the flashrom log\n"
+ "file for all operations you tested (see the man page for details), and mention\n"
+ "which mainboard or programmer you tested in the subject line.\n"
+ "Thanks for your help!\n");
+ }
+}
+
diff --git a/cli_output.c b/cli_output.c
new file mode 100644
index 0000000..feafbd2
--- /dev/null
+++ b/cli_output.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ * Copyright (C) 2011 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include "flash.h"
+
+int verbose_screen = MSG_INFO;
+int verbose_logfile = MSG_DEBUG2;
+
+#ifndef STANDALONE
+static FILE *logfile = NULL;
+
+int close_logfile(void)
+{
+ if (!logfile)
+ return 0;
+ /* No need to call fflush() explicitly, fclose() already does that. */
+ if (fclose(logfile)) {
+ /* fclose returned an error. Stop writing to be safe. */
+ logfile = NULL;
+ msg_gerr("Closing the log file returned error %s\n", strerror(errno));
+ return 1;
+ }
+ logfile = NULL;
+ return 0;
+}
+
+int open_logfile(const char * const filename)
+{
+ if (!filename) {
+ msg_gerr("No logfile name specified.\n");
+ return 1;
+ }
+ if ((logfile = fopen(filename, "w")) == NULL) {
+ msg_gerr("Error: opening log file \"%s\" failed: %s\n", filename, strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+void start_logging(void)
+{
+ enum msglevel oldverbose_screen = verbose_screen;
+
+ /* Shut up the console. */
+ verbose_screen = MSG_ERROR;
+ print_version();
+ verbose_screen = oldverbose_screen;
+}
+#endif /* !STANDALONE */
+
+/* Please note that level is the verbosity, not the importance of the message. */
+int print(enum msglevel level, const char *fmt, ...)
+{
+ va_list ap;
+ int ret = 0;
+ FILE *output_type = stdout;
+
+ if (level < MSG_INFO)
+ output_type = stderr;
+
+ if (level <= verbose_screen) {
+ va_start(ap, fmt);
+ ret = vfprintf(output_type, fmt, ap);
+ va_end(ap);
+ /* msg_*spew often happens inside chip accessors in possibly
+ * time-critical operations. Don't slow them down by flushing. */
+ if (level != MSG_SPEW)
+ fflush(output_type);
+ }
+#ifndef STANDALONE
+ if ((level <= verbose_logfile) && logfile) {
+ va_start(ap, fmt);
+ ret = vfprintf(logfile, fmt, ap);
+ va_end(ap);
+ if (level != MSG_SPEW)
+ fflush(logfile);
+ }
+#endif /* !STANDALONE */
+ return ret;
+}
diff --git a/coreboot_tables.h b/coreboot_tables.h
new file mode 100644
index 0000000..2e96526
--- /dev/null
+++ b/coreboot_tables.h
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
+ * Copyright (C) 2005-2007 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#ifndef COREBOOT_TABLES_H
+#define COREBOOT_TABLES_H
+
+#include <stdint.h>
+
+/* The coreboot table information is for conveying information
+ * from the firmware to the loaded OS image. Primarily this
+ * is expected to be information that cannot be discovered by
+ * other means, such as querying the hardware directly.
+ *
+ * All of the information should be Position Independent Data.
+ * That is it should be safe to relocated any of the information
+ * without it's meaning/correctness changing. For table that
+ * can reasonably be used on multiple architectures the data
+ * size should be fixed. This should ease the transition between
+ * 32 bit and 64 bit architectures etc.
+ *
+ * The completeness test for the information in this table is:
+ * - Can all of the hardware be detected?
+ * - Are the per motherboard constants available?
+ * - Is there enough to allow a kernel to run that was written before
+ * a particular motherboard is constructed? (Assuming the kernel
+ * has drivers for all of the hardware but it does not have
+ * assumptions on how the hardware is connected together).
+ *
+ * With this test it should be straight forward to determine if a
+ * table entry is required or not. This should remove much of the
+ * long term compatibility burden as table entries which are
+ * irrelevant or have been replaced by better alternatives may be
+ * dropped. Of course it is polite and expedite to include extra
+ * table entries and be backwards compatible, but it is not required.
+ */
+
+/* Since coreboot is usually compiled 32bit, gcc will align 64bit
+ * types to 32bit boundaries. If the coreboot table is dumped on a
+ * 64bit system, a uint64_t would be aligned to 64bit boundaries,
+ * breaking the table format.
+ *
+ * lb_uint64 will keep 64bit coreboot table values aligned to 32bit
+ * to ensure compatibility. They can be accessed with the two functions
+ * below: unpack_lb64() and pack_lb64()
+ *
+ * See also: util/lbtdump/lbtdump.c
+ */
+
+struct lb_uint64 {
+ uint32_t lo;
+ uint32_t hi;
+};
+
+struct lb_header {
+ uint8_t signature[4]; /* LBIO */
+ uint32_t header_bytes;
+ uint32_t header_checksum;
+ uint32_t table_bytes;
+ uint32_t table_checksum;
+ uint32_t table_entries;
+};
+
+/* Every entry in the boot environment list will correspond to a boot
+ * info record. Encoding both type and size. The type is obviously
+ * so you can tell what it is. The size allows you to skip that
+ * boot environment record if you don't know what it easy. This allows
+ * forward compatibility with records not yet defined.
+ */
+struct lb_record {
+ uint32_t tag; /* tag ID */
+ uint32_t size; /* size of record (in bytes) */
+};
+
+#define LB_TAG_UNUSED 0x0000
+
+#define LB_TAG_MEMORY 0x0001
+
+struct lb_memory_range {
+ struct lb_uint64 start;
+ struct lb_uint64 size;
+ uint32_t type;
+#define LB_MEM_RAM 1 /* Memory anyone can use */
+#define LB_MEM_RESERVED 2 /* Don't use this memory region */
+#define LB_MEM_TABLE 16 /* Ram configuration tables are kept in */
+};
+
+struct lb_memory {
+ uint32_t tag;
+ uint32_t size;
+ struct lb_memory_range map[0];
+};
+
+#define LB_TAG_HWRPB 0x0002
+struct lb_hwrpb {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t hwrpb;
+};
+
+#define LB_TAG_MAINBOARD 0x0003
+struct lb_mainboard {
+ uint32_t tag;
+ uint32_t size;
+ uint8_t vendor_idx;
+ uint8_t part_number_idx;
+ uint8_t strings[0];
+};
+
+#define LB_TAG_VERSION 0x0004
+#define LB_TAG_EXTRA_VERSION 0x0005
+#define LB_TAG_BUILD 0x0006
+#define LB_TAG_COMPILE_TIME 0x0007
+#define LB_TAG_COMPILE_BY 0x0008
+#define LB_TAG_COMPILE_HOST 0x0009
+#define LB_TAG_COMPILE_DOMAIN 0x000a
+#define LB_TAG_COMPILER 0x000b
+#define LB_TAG_LINKER 0x000c
+#define LB_TAG_ASSEMBLER 0x000d
+struct lb_string {
+ uint32_t tag;
+ uint32_t size;
+ uint8_t string[0];
+};
+
+#define LB_TAG_FORWARD 0x0011
+struct lb_forward {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t forward;
+};
+
+#endif /* COREBOOT_TABLES_H */
diff --git a/dediprog.c b/dediprog.c
new file mode 100644
index 0000000..019de46
--- /dev/null
+++ b/dediprog.c
@@ -0,0 +1,1090 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2015 Simon Glass
+ * Copyright (C) 2015 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "platform.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <libusb.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* LIBUSB_CALL ensures the right calling conventions on libusb callbacks.
+ * However, the macro is not defined everywhere. m(
+ */
+#ifndef LIBUSB_CALL
+#define LIBUSB_CALL
+#endif
+
+#define FIRMWARE_VERSION(x,y,z) ((x << 16) | (y << 8) | z)
+#define DEFAULT_TIMEOUT 3000
+#define DEDIPROG_ASYNC_TRANSFERS 8 /* at most 8 asynchronous transfers */
+#define REQTYPE_OTHER_OUT (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER) /* 0x43 */
+#define REQTYPE_OTHER_IN (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_OTHER) /* 0xC3 */
+#define REQTYPE_EP_OUT (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT) /* 0x42 */
+#define REQTYPE_EP_IN (LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT) /* 0xC2 */
+struct libusb_context *usb_ctx;
+static libusb_device_handle *dediprog_handle;
+static int dediprog_in_endpoint;
+static int dediprog_out_endpoint;
+
+enum dediprog_devtype {
+ DEV_UNKNOWN = 0,
+ DEV_SF100 = 100,
+ DEV_SF600 = 600,
+};
+
+enum dediprog_leds {
+ LED_INVALID = -1,
+ LED_NONE = 0,
+ LED_PASS = 1 << 0,
+ LED_BUSY = 1 << 1,
+ LED_ERROR = 1 << 2,
+ LED_ALL = 7,
+};
+
+/* IO bits for CMD_SET_IO_LED message */
+enum dediprog_ios {
+ IO1 = 1 << 0,
+ IO2 = 1 << 1,
+ IO3 = 1 << 2,
+ IO4 = 1 << 3,
+};
+
+enum dediprog_cmds {
+ CMD_TRANSCEIVE = 0x01,
+ CMD_POLL_STATUS_REG = 0x02,
+ CMD_SET_VPP = 0x03,
+ CMD_SET_TARGET = 0x04,
+ CMD_READ_EEPROM = 0x05,
+ CMD_WRITE_EEPROM = 0x06,
+ CMD_SET_IO_LED = 0x07,
+ CMD_READ_PROG_INFO = 0x08,
+ CMD_SET_VCC = 0x09,
+ CMD_SET_STANDALONE = 0x0A,
+ CMD_SET_VOLTAGE = 0x0B, /* Only in firmware older than 6.0.0 */
+ CMD_GET_BUTTON = 0x11,
+ CMD_GET_UID = 0x12,
+ CMD_SET_CS = 0x14,
+ CMD_IO_MODE = 0x15,
+ CMD_FW_UPDATE = 0x1A,
+ CMD_FPGA_UPDATE = 0x1B,
+ CMD_READ_FPGA_VERSION = 0x1C,
+ CMD_SET_HOLD = 0x1D,
+ CMD_READ = 0x20,
+ CMD_WRITE = 0x30,
+ CMD_WRITE_AT45DB = 0x31,
+ CMD_NAND_WRITE = 0x32,
+ CMD_NAND_READ = 0x33,
+ CMD_SET_SPI_CLK = 0x61,
+ CMD_CHECK_SOCKET = 0x62,
+ CMD_DOWNLOAD_PRJ = 0x63,
+ CMD_READ_PRJ_NAME = 0x64,
+ // New protocol/firmware only
+ CMD_CHECK_SDCARD = 0x65,
+ CMD_READ_PRJ = 0x66,
+};
+
+enum dediprog_target {
+ FLASH_TYPE_APPLICATION_FLASH_1 = 0,
+ FLASH_TYPE_FLASH_CARD,
+ FLASH_TYPE_APPLICATION_FLASH_2,
+ FLASH_TYPE_SOCKET,
+};
+
+enum dediprog_readmode {
+ READ_MODE_STD = 1,
+ READ_MODE_FAST = 2,
+ READ_MODE_ATMEL45 = 3,
+ READ_MODE_4B_ADDR_FAST = 4,
+ READ_MODE_4B_ADDR_FAST_0x0C = 5, /* New protocol only */
+};
+
+enum dediprog_writemode {
+ WRITE_MODE_PAGE_PGM = 1,
+ WRITE_MODE_PAGE_WRITE = 2,
+ WRITE_MODE_1B_AAI = 3,
+ WRITE_MODE_2B_AAI = 4,
+ WRITE_MODE_128B_PAGE = 5,
+ WRITE_MODE_PAGE_AT26DF041 = 6,
+ WRITE_MODE_SILICON_BLUE_FPGA = 7,
+ WRITE_MODE_64B_PAGE_NUMONYX_PCM = 8, /* unit of 512 bytes */
+ WRITE_MODE_4B_ADDR_256B_PAGE_PGM = 9,
+ WRITE_MODE_32B_PAGE_PGM_MXIC_512K = 10, /* unit of 512 bytes */
+ WRITE_MODE_4B_ADDR_256B_PAGE_PGM_0x12 = 11,
+ WRITE_MODE_4B_ADDR_256B_PAGE_PGM_FLAGS = 12,
+};
+
+enum dediprog_standalone_mode {
+ ENTER_STANDALONE_MODE = 0,
+ LEAVE_STANDALONE_MODE = 1,
+};
+
+const struct dev_entry devs_dediprog[] = {
+ {0x0483, 0xDADA, OK, "Dediprog", "SF100/SF600"},
+
+ {0},
+};
+
+static int dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
+enum dediprog_devtype dediprog_devicetype = DEV_UNKNOWN;
+
+#if defined(LIBUSB_MAJOR) && defined(LIBUSB_MINOR) && defined(LIBUSB_MICRO) && \
+ LIBUSB_MAJOR <= 1 && LIBUSB_MINOR == 0 && LIBUSB_MICRO < 9
+/* Quick and dirty replacement for missing libusb_error_name in libusb < 1.0.9 */
+const char * LIBUSB_CALL libusb_error_name(int error_code)
+{
+ if (error_code >= INT16_MIN && error_code <= INT16_MAX) {
+ /* 18 chars for text, rest for number (16 b should be enough), sign, nullbyte. */
+ static char my_libusb_error[18 + 5 + 2];
+ sprintf(my_libusb_error, "libusb error code %i", error_code);
+ return my_libusb_error;
+ } else {
+ return "UNKNOWN";
+ }
+}
+#endif
+
+/* Returns true if firmware (and thus hardware) supports the "new" protocol */
+static bool is_new_prot(void)
+{
+ switch (dediprog_devicetype) {
+ case DEV_SF100:
+ return dediprog_firmwareversion >= FIRMWARE_VERSION(5, 5, 0);
+ case DEV_SF600:
+ return dediprog_firmwareversion >= FIRMWARE_VERSION(6, 9, 0);
+ default:
+ return 0;
+ }
+}
+
+struct dediprog_transfer_status {
+ int error; /* OK if 0, ERROR else */
+ unsigned int queued_idx;
+ unsigned int finished_idx;
+};
+
+static void LIBUSB_CALL dediprog_bulk_read_cb(struct libusb_transfer *const transfer)
+{
+ struct dediprog_transfer_status *const status = (struct dediprog_transfer_status *)transfer->user_data;
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ status->error = 1;
+ msg_perr("SPI bulk read failed!\n");
+ }
+ ++status->finished_idx;
+}
+
+static int dediprog_bulk_read_poll(const struct dediprog_transfer_status *const status, const int finish)
+{
+ if (status->finished_idx >= status->queued_idx)
+ return 0;
+
+ do {
+ struct timeval timeout = { 10, 0 };
+ const int ret = libusb_handle_events_timeout(usb_ctx, &timeout);
+ if (ret < 0) {
+ msg_perr("Polling read events failed: %i %s!\n", ret, libusb_error_name(ret));
+ return 1;
+ }
+ } while (finish && (status->finished_idx < status->queued_idx));
+ return 0;
+}
+
+static int dediprog_read(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, uint8_t *bytes, size_t size)
+{
+ return libusb_control_transfer(dediprog_handle, REQTYPE_EP_IN, cmd, value, idx,
+ (unsigned char *)bytes, size, DEFAULT_TIMEOUT);
+}
+
+static int dediprog_write(enum dediprog_cmds cmd, unsigned int value, unsigned int idx, const uint8_t *bytes, size_t size)
+{
+ return libusb_control_transfer(dediprog_handle, REQTYPE_EP_OUT, cmd, value, idx,
+ (unsigned char *)bytes, size, DEFAULT_TIMEOUT);
+}
+
+
+/* Might be useful for other USB devices as well. static for now.
+ * num parameter allows user to specify one device of multiple installed */
+static struct libusb_device_handle *get_device_by_vid_pid_number(uint16_t vid, uint16_t pid, unsigned int num)
+{
+ struct libusb_device **list;
+ ssize_t count = libusb_get_device_list(usb_ctx, &list);
+ if (count < 0) {
+ msg_perr("Getting the USB device list failed (%s)!\n", libusb_error_name(count));
+ return NULL;
+ }
+
+ struct libusb_device_handle *handle = NULL;
+ ssize_t i = 0;
+ for (i = 0; i < count; i++) {
+ struct libusb_device *dev = list[i];
+ struct libusb_device_descriptor desc;
+ int err = libusb_get_device_descriptor(dev, &desc);
+ if (err != 0) {
+ msg_perr("Reading the USB device descriptor failed (%s)!\n", libusb_error_name(err));
+ libusb_free_device_list(list, 1);
+ return NULL;
+ }
+ if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
+ msg_pdbg("Found USB device %04"PRIx16":%04"PRIx16" at address %d-%d.\n",
+ desc.idVendor, desc.idProduct,
+ libusb_get_bus_number(dev), libusb_get_device_address(dev));
+ if (num == 0) {
+ err = libusb_open(dev, &handle);
+ if (err != 0) {
+ msg_perr("Opening the USB device failed (%s)!\n",
+ libusb_error_name(err));
+ libusb_free_device_list(list, 1);
+ return NULL;
+ }
+ break;
+ }
+ num--;
+ }
+ }
+ libusb_free_device_list(list, 1);
+
+ return handle;
+}
+
+/* This function sets the GPIOs connected to the LEDs as well as IO1-IO4. */
+static int dediprog_set_leds(int leds)
+{
+ if (leds < LED_NONE || leds > LED_ALL)
+ leds = LED_ALL;
+
+ /* Older Dediprogs with 2.x.x and 3.x.x firmware only had two LEDs, assigned to different bits. So map
+ * them around if we have an old device. On those devices the LEDs map as follows:
+ * bit 2 == 0: green light is on.
+ * bit 0 == 0: red light is on.
+ *
+ * Additionally, the command structure has changed with the "new" protocol.
+ *
+ * FIXME: take IO pins into account
+ */
+ int target_leds, ret;
+ if (is_new_prot()) {
+ target_leds = (leds ^ 7) << 8;
+ ret = dediprog_write(CMD_SET_IO_LED, target_leds, 0, NULL, 0);
+ } else {
+ if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 0, 0)) {
+ target_leds = ((leds & LED_ERROR) >> 2) | ((leds & LED_PASS) << 2);
+ } else {
+ target_leds = leds;
+ }
+ target_leds ^= 7;
+
+ ret = dediprog_write(CMD_SET_IO_LED, 0x9, target_leds, NULL, 0);
+ }
+
+ if (ret != 0x0) {
+ msg_perr("Command Set LED 0x%x failed (%s)!\n", leds, libusb_error_name(ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dediprog_set_spi_voltage(int millivolt)
+{
+ int ret;
+ uint16_t voltage_selector;
+
+ switch (millivolt) {
+ case 0:
+ /* Admittedly this one is an assumption. */
+ voltage_selector = 0x0;
+ break;
+ case 1800:
+ voltage_selector = 0x12;
+ break;
+ case 2500:
+ voltage_selector = 0x11;
+ break;
+ case 3500:
+ voltage_selector = 0x10;
+ break;
+ default:
+ msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
+ return 1;
+ }
+ msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
+ millivolt % 1000);
+
+ if (voltage_selector == 0) {
+ /* Wait some time as the original driver does. */
+ programmer_delay(200 * 1000);
+ }
+ ret = dediprog_write(CMD_SET_VCC, voltage_selector, 0, NULL, 0);
+ if (ret != 0x0) {
+ msg_perr("Command Set SPI Voltage 0x%x failed!\n",
+ voltage_selector);
+ return 1;
+ }
+ if (voltage_selector != 0) {
+ /* Wait some time as the original driver does. */
+ programmer_delay(200 * 1000);
+ }
+ return 0;
+}
+
+struct dediprog_spispeeds {
+ const char *const name;
+ const int speed;
+};
+
+static const struct dediprog_spispeeds spispeeds[] = {
+ { "24M", 0x0 },
+ { "12M", 0x2 },
+ { "8M", 0x1 },
+ { "3M", 0x3 },
+ { "2.18M", 0x4 },
+ { "1.5M", 0x5 },
+ { "750k", 0x6 },
+ { "375k", 0x7 },
+ { NULL, 0x0 },
+};
+
+static int dediprog_set_spi_speed(unsigned int spispeed_idx)
+{
+ if (dediprog_firmwareversion < FIRMWARE_VERSION(5, 0, 0)) {
+ msg_pwarn("Skipping to set SPI speed because firmware is too old.\n");
+ return 0;
+ }
+
+ const struct dediprog_spispeeds *spispeed = &spispeeds[spispeed_idx];
+ msg_pdbg("SPI speed is %sHz\n", spispeed->name);
+
+ int ret = dediprog_write(CMD_SET_SPI_CLK, spispeed->speed, 0, NULL, 0);
+ if (ret != 0x0) {
+ msg_perr("Command Set SPI Speed 0x%x failed!\n", spispeed->speed);
+ return 1;
+ }
+ return 0;
+}
+
+static void fill_rw_cmd_payload(uint8_t *data_packet, unsigned int count, uint8_t dedi_spi_cmd, unsigned int *value, unsigned int *idx, unsigned int start) {
+ /* First 5 bytes are common in both generations. */
+ data_packet[0] = count & 0xff;
+ data_packet[1] = (count >> 8) & 0xff;
+ data_packet[2] = 0; /* RFU */
+ data_packet[3] = dedi_spi_cmd; /* Read/Write Mode (currently READ_MODE_STD, WRITE_MODE_PAGE_PGM or WRITE_MODE_2B_AAI) */
+ data_packet[4] = 0; /* "Opcode". Specs imply necessity only for READ_MODE_4B_ADDR_FAST and WRITE_MODE_4B_ADDR_256B_PAGE_PGM */
+
+ if (is_new_prot()) {
+ *value = *idx = 0;
+ data_packet[5] = 0; /* RFU */
+ data_packet[6] = (start >> 0) & 0xff;
+ data_packet[7] = (start >> 8) & 0xff;
+ data_packet[8] = (start >> 16) & 0xff;
+ data_packet[9] = (start >> 24) & 0xff;
+ } else {
+ *value = start % 0x10000;
+ *idx = start / 0x10000;
+ }
+}
+
+/* Bulk read interface, will read multiple 512 byte chunks aligned to 512 bytes.
+ * @start start address
+ * @len length
+ * @return 0 on success, 1 on failure
+ */
+static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+ int err = 1;
+
+ /* chunksize must be 512, other sizes will NOT work at all. */
+ const unsigned int chunksize = 512;
+ const unsigned int count = len / chunksize;
+
+ struct dediprog_transfer_status status = { 0, 0, 0 };
+ struct libusb_transfer *transfers[DEDIPROG_ASYNC_TRANSFERS] = { NULL, };
+ struct libusb_transfer *transfer;
+
+ if ((start % chunksize) || (len % chunksize)) {
+ msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug at flashrom@flashrom.org\n",
+ __func__, start, len);
+ return 1;
+ }
+
+ if (len == 0)
+ return 0;
+
+ /* Command packet size of protocols: new 10 B, old 5 B. */
+ uint8_t data_packet[is_new_prot() ? 10 : 5];
+ unsigned int value, idx;
+ fill_rw_cmd_payload(data_packet, count, READ_MODE_STD, &value, &idx, start);
+
+ int ret = dediprog_write(CMD_READ, value, idx, data_packet, sizeof(data_packet));
+ if (ret != sizeof(data_packet)) {
+ msg_perr("Command Read SPI Bulk failed, %i %s!\n", ret, libusb_error_name(ret));
+ return 1;
+ }
+
+ /*
+ * Ring buffer of bulk transfers.
+ * Poll until at least one transfer is ready,
+ * schedule next transfers until buffer is full.
+ */
+
+ /* Allocate bulk transfers. */
+ unsigned int i;
+ for (i = 0; i < min(DEDIPROG_ASYNC_TRANSFERS, count); ++i) {
+ transfers[i] = libusb_alloc_transfer(0);
+ if (!transfers[i]) {
+ msg_perr("Allocating libusb transfer %i failed: %s!\n", i, libusb_error_name(ret));
+ goto err_free;
+ }
+ }
+
+ /* Now transfer requested chunks using libusb's asynchronous interface. */
+ while (!status.error && (status.queued_idx < count)) {
+ while ((status.queued_idx - status.finished_idx) < DEDIPROG_ASYNC_TRANSFERS) {
+ transfer = transfers[status.queued_idx % DEDIPROG_ASYNC_TRANSFERS];
+ libusb_fill_bulk_transfer(transfer, dediprog_handle, 0x80 | dediprog_in_endpoint,
+ (unsigned char *)buf + status.queued_idx * chunksize, chunksize,
+ dediprog_bulk_read_cb, &status, DEFAULT_TIMEOUT);
+ transfer->flags |= LIBUSB_TRANSFER_SHORT_NOT_OK;
+ ret = libusb_submit_transfer(transfer);
+ if (ret < 0) {
+ msg_perr("Submitting SPI bulk read %i failed: %s!\n",
+ status.queued_idx, libusb_error_name(ret));
+ goto err_free;
+ }
+ ++status.queued_idx;
+ }
+ if (dediprog_bulk_read_poll(&status, 0))
+ goto err_free;
+ }
+ /* Wait for transfers to finish. */
+ if (dediprog_bulk_read_poll(&status, 1))
+ goto err_free;
+ /* Check if everything has been transmitted. */
+ if ((status.finished_idx < count) || status.error)
+ goto err_free;
+
+ err = 0;
+
+err_free:
+ dediprog_bulk_read_poll(&status, 1);
+ for (i = 0; i < DEDIPROG_ASYNC_TRANSFERS; ++i)
+ if (transfers[i]) libusb_free_transfer(transfers[i]);
+ return err;
+}
+
+static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+ int ret;
+ /* chunksize must be 512, other sizes will NOT work at all. */
+ const unsigned int chunksize = 0x200;
+ unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
+ unsigned int bulklen;
+
+ dediprog_set_leds(LED_BUSY);
+
+ if (residue) {
+ msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n",
+ start, residue);
+ ret = spi_read_chunked(flash, buf, start, residue, 16);
+ if (ret)
+ goto err;
+ }
+
+ /* Round down. */
+ bulklen = (len - residue) / chunksize * chunksize;
+ ret = dediprog_spi_bulk_read(flash, buf + residue, start + residue, bulklen);
+ if (ret)
+ goto err;
+
+ len -= residue + bulklen;
+ if (len != 0) {
+ msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n",
+ start, len);
+ ret = spi_read_chunked(flash, buf + residue + bulklen,
+ start + residue + bulklen, len, 16);
+ if (ret)
+ goto err;
+ }
+
+ dediprog_set_leds(LED_PASS);
+ return 0;
+err:
+ dediprog_set_leds(LED_ERROR);
+ return ret;
+}
+
+/* Bulk write interface, will write multiple chunksize byte chunks aligned to chunksize bytes.
+ * @chunksize length of data chunks, only 256 supported by now
+ * @start start address
+ * @len length
+ * @dedi_spi_cmd dediprog specific write command for spi bus
+ * @return 0 on success, 1 on failure
+ */
+static int dediprog_spi_bulk_write(struct flashctx *flash, const uint8_t *buf, unsigned int chunksize,
+ unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
+{
+ /* USB transfer size must be 512, other sizes will NOT work at all.
+ * chunksize is the real data size per USB bulk transfer. The remaining
+ * space in a USB bulk transfer must be filled with 0xff padding.
+ */
+ const unsigned int count = len / chunksize;
+
+ /*
+ * We should change this check to
+ * chunksize > 512
+ * once we know how to handle different chunk sizes.
+ */
+ if (chunksize != 256) {
+ msg_perr("%s: Chunk sizes other than 256 bytes are unsupported, chunksize=%u!\n"
+ "Please report a bug at flashrom@flashrom.org\n", __func__, chunksize);
+ return 1;
+ }
+
+ if ((start % chunksize) || (len % chunksize)) {
+ msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug "
+ "at flashrom@flashrom.org\n", __func__, start, len);
+ return 1;
+ }
+
+ /* No idea if the hardware can handle empty writes, so chicken out. */
+ if (len == 0)
+ return 0;
+
+ /* Command packet size of protocols: new 10 B, old 5 B. */
+ uint8_t data_packet[is_new_prot() ? 10 : 5];
+ unsigned int value, idx;
+ fill_rw_cmd_payload(data_packet, count, dedi_spi_cmd, &value, &idx, start);
+ int ret = dediprog_write(CMD_WRITE, value, idx, data_packet, sizeof(data_packet));
+ if (ret != sizeof(data_packet)) {
+ msg_perr("Command Write SPI Bulk failed, %s!\n", libusb_error_name(ret));
+ return 1;
+ }
+
+ unsigned int i;
+ for (i = 0; i < count; i++) {
+ unsigned char usbbuf[512];
+ memcpy(usbbuf, buf + i * chunksize, chunksize);
+ memset(usbbuf + chunksize, 0xff, sizeof(usbbuf) - chunksize); // fill up with 0xFF
+ int transferred;
+ ret = libusb_bulk_transfer(dediprog_handle, dediprog_out_endpoint, usbbuf, 512, &transferred,
+ DEFAULT_TIMEOUT);
+ if ((ret < 0) || (transferred != 512)) {
+ msg_perr("SPI bulk write failed, expected %i, got %s!\n", 512, libusb_error_name(ret));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int dediprog_spi_write(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
+{
+ int ret;
+ const unsigned int chunksize = flash->chip->page_size;
+ unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
+ unsigned int bulklen;
+
+ dediprog_set_leds(LED_BUSY);
+
+ if (chunksize != 256) {
+ msg_pdbg("Page sizes other than 256 bytes are unsupported as "
+ "we don't know how dediprog\nhandles them.\n");
+ /* Write everything like it was residue. */
+ residue = len;
+ }
+
+ if (residue) {
+ msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
+ start, residue);
+ /* No idea about the real limit. Maybe 12, maybe more. */
+ ret = spi_write_chunked(flash, buf, start, residue, 12);
+ if (ret) {
+ dediprog_set_leds(LED_ERROR);
+ return ret;
+ }
+ }
+
+ /* Round down. */
+ bulklen = (len - residue) / chunksize * chunksize;
+ ret = dediprog_spi_bulk_write(flash, buf + residue, chunksize, start + residue, bulklen, dedi_spi_cmd);
+ if (ret) {
+ dediprog_set_leds(LED_ERROR);
+ return ret;
+ }
+
+ len -= residue + bulklen;
+ if (len) {
+ msg_pdbg("Slow write for partial block from 0x%x, length 0x%x\n",
+ start, len);
+ ret = spi_write_chunked(flash, buf + residue + bulklen,
+ start + residue + bulklen, len, 12);
+ if (ret) {
+ dediprog_set_leds(LED_ERROR);
+ return ret;
+ }
+ }
+
+ dediprog_set_leds(LED_PASS);
+ return 0;
+}
+
+static int dediprog_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return dediprog_spi_write(flash, buf, start, len, WRITE_MODE_PAGE_PGM);
+}
+
+static int dediprog_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return dediprog_spi_write(flash, buf, start, len, WRITE_MODE_2B_AAI);
+}
+
+static int dediprog_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int ret;
+
+ msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
+ if (writecnt > flash->mst->spi.max_data_write) {
+ msg_perr("Invalid writecnt=%i, aborting.\n", writecnt);
+ return 1;
+ }
+ if (readcnt > flash->mst->spi.max_data_read) {
+ msg_perr("Invalid readcnt=%i, aborting.\n", readcnt);
+ return 1;
+ }
+
+ unsigned int idx, value;
+ /* New protocol has options and timeout combined as value while the old one used the value field for
+ * timeout and the index field for options. */
+ if (is_new_prot()) {
+ idx = 0;
+ value = readcnt ? 0x1 : 0x0; // Indicate if we require a read
+ } else {
+ idx = readcnt ? 0x1 : 0x0; // Indicate if we require a read
+ value = 0;
+ }
+ ret = dediprog_write(CMD_TRANSCEIVE, value, idx, writearr, writecnt);
+ if (ret != writecnt) {
+ msg_perr("Send SPI failed, expected %i, got %i %s!\n",
+ writecnt, ret, libusb_error_name(ret));
+ return 1;
+ }
+ if (readcnt == 0) // If we don't require a response, we are done here
+ return 0;
+
+ /* The specifications do state the possibility to set a timeout for transceive transactions.
+ * Apparently the "timeout" is a delay, and you can use long delays to accelerate writing - in case you
+ * can predict the time needed by the previous command or so (untested). In any case, using this
+ * "feature" to set sane-looking timouts for the read below will completely trash performance with
+ * SF600 and/or firmwares >= 6.0 while they seem to be benign on SF100 with firmwares <= 5.5.2. *shrug*
+ *
+ * The specification also uses only 0 in its examples, so the lesson to learn here:
+ * "Never trust the description of an interface in the documentation but use the example code and pray."
+ const uint8_t read_timeout = 10 + readcnt/512;
+ if (is_new_prot()) {
+ idx = 0;
+ value = min(read_timeout, 0xFF) | (0 << 8) ; // Timeout in lower byte, option in upper byte
+ } else {
+ idx = (0 & 0xFF); // Lower byte is option (0x01 = require SR, 0x02 keep CS low)
+ value = min(read_timeout, 0xFF); // Possibly two bytes but we play safe here
+ }
+ ret = dediprog_read(CMD_TRANSCEIVE, value, idx, readarr, readcnt);
+ */
+ ret = dediprog_read(CMD_TRANSCEIVE, 0, 0, readarr, readcnt);
+ if (ret != readcnt) {
+ msg_perr("Receive SPI failed, expected %i, got %i %s!\n", readcnt, ret, libusb_error_name(ret));
+ return 1;
+ }
+ return 0;
+}
+
+static int dediprog_check_devicestring(void)
+{
+ int ret;
+ char buf[0x11];
+
+ /* Command Receive Device String. */
+ ret = dediprog_read(CMD_READ_PROG_INFO, 0, 0, (uint8_t *)buf, 0x10);
+ if (ret != 0x10) {
+ msg_perr("Incomplete/failed Command Receive Device String!\n");
+ return 1;
+ }
+ buf[0x10] = '\0';
+ msg_pdbg("Found a %s\n", buf);
+ if (memcmp(buf, "SF100", 0x5) == 0)
+ dediprog_devicetype = DEV_SF100;
+ else if (memcmp(buf, "SF600", 0x5) == 0)
+ dediprog_devicetype = DEV_SF600;
+ else {
+ msg_perr("Device not a SF100 or SF600!\n");
+ return 1;
+ }
+
+ int sfnum;
+ int fw[3];
+ if (sscanf(buf, "SF%d V:%d.%d.%d ", &sfnum, &fw[0], &fw[1], &fw[2]) != 4 ||
+ sfnum != dediprog_devicetype) {
+ msg_perr("Unexpected firmware version string '%s'\n", buf);
+ return 1;
+ }
+ /* Only these major versions were tested. */
+ if (fw[0] < 2 || fw[0] > 7) {
+ msg_perr("Unexpected firmware version %d.%d.%d!\n", fw[0], fw[1], fw[2]);
+ return 1;
+ }
+ dediprog_firmwareversion = FIRMWARE_VERSION(fw[0], fw[1], fw[2]);
+
+ return 0;
+}
+
+/*
+ * This command presumably sets the voltage for the SF100 itself (not the
+ * SPI flash). Only use this command with firmware older than V6.0.0. Newer
+ * (including all SF600s) do not support it.
+ */
+
+/* This command presumably sets the voltage for the SF100 itself (not the SPI flash).
+ * Only use dediprog_set_voltage on SF100 programmers with firmware older
+ * than V6.0.0. Newer programmers (including all SF600s) do not support it. */
+static int dediprog_set_voltage(void)
+{
+ unsigned char buf[1] = {0};
+ int ret = libusb_control_transfer(dediprog_handle, REQTYPE_OTHER_IN, CMD_SET_VOLTAGE, 0x0, 0x0,
+ buf, 0x1, DEFAULT_TIMEOUT);
+ if (ret < 0) {
+ msg_perr("Command Set Voltage failed (%s)!\n", libusb_error_name(ret));
+ return 1;
+ }
+ if ((ret != 1) || (buf[0] != 0x6f)) {
+ msg_perr("Unexpected response to init!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dediprog_standalone_mode(void)
+{
+ int ret;
+
+ if (dediprog_devicetype != DEV_SF600)
+ return 0;
+
+ msg_pdbg2("Disabling standalone mode.\n");
+ ret = dediprog_write(CMD_SET_STANDALONE, LEAVE_STANDALONE_MODE, 0, NULL, 0);
+ if (ret) {
+ msg_perr("Failed to disable standalone mode: %s\n", libusb_error_name(ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+/* Something.
+ * Present in eng_detect_blink.log with firmware 3.1.8
+ * Always preceded by Command Receive Device String
+ */
+static int dediprog_command_b(void)
+{
+ int ret;
+ char buf[0x3];
+
+ ret = usb_control_msg(dediprog_handle, REQTYPE_OTHER_IN, 0x7, 0x0, 0xef00,
+ buf, 0x3, DEFAULT_TIMEOUT);
+ if (ret < 0) {
+ msg_perr("Command B failed (%s)!\n", libusb_error_name(ret));
+ return 1;
+ }
+ if ((ret != 0x3) || (buf[0] != 0xff) || (buf[1] != 0xff) ||
+ (buf[2] != 0xff)) {
+ msg_perr("Unexpected response to Command B!\n");
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+static int set_target_flash(enum dediprog_target target)
+{
+ int ret = dediprog_write(CMD_SET_TARGET, target, 0, NULL, 0);
+ if (ret != 0) {
+ msg_perr("set_target_flash failed (%s)!\n", libusb_error_name(ret));
+ return 1;
+ }
+ return 0;
+}
+
+#if 0
+/* Returns true if the button is currently pressed. */
+static bool dediprog_get_button(void)
+{
+ char buf[1];
+ int ret = usb_control_msg(dediprog_handle, REQTYPE_EP_IN, CMD_GET_BUTTON, 0, 0,
+ buf, 0x1, DEFAULT_TIMEOUT);
+ if (ret != 0) {
+ msg_perr("Could not get button state (%s)!\n", libusb_error_name(ret));
+ return 1;
+ }
+ return buf[0] != 1;
+}
+#endif
+
+static int parse_voltage(char *voltage)
+{
+ char *tmp = NULL;
+ int i;
+ int millivolt = 0, fraction = 0;
+
+ if (!voltage || !strlen(voltage)) {
+ msg_perr("Empty voltage= specified.\n");
+ return -1;
+ }
+ millivolt = (int)strtol(voltage, &tmp, 0);
+ voltage = tmp;
+ /* Handle "," and "." as decimal point. Everything after it is assumed
+ * to be in decimal notation.
+ */
+ if ((*voltage == '.') || (*voltage == ',')) {
+ voltage++;
+ for (i = 0; i < 3; i++) {
+ fraction *= 10;
+ /* Don't advance if the current character is invalid,
+ * but continue multiplying.
+ */
+ if ((*voltage < '0') || (*voltage > '9'))
+ continue;
+ fraction += *voltage - '0';
+ voltage++;
+ }
+ /* Throw away remaining digits. */
+ voltage += strspn(voltage, "0123456789");
+ }
+ /* The remaining string must be empty or "mV" or "V". */
+ tolower_string(voltage);
+
+ /* No unit or "V". */
+ if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
+ millivolt *= 1000;
+ millivolt += fraction;
+ } else if (!strncmp(voltage, "mv", 2) ||
+ !strncmp(voltage, "milliv", 6)) {
+ /* No adjustment. fraction is discarded. */
+ } else {
+ /* Garbage at the end of the string. */
+ msg_perr("Garbage voltage= specified.\n");
+ return -1;
+ }
+ return millivolt;
+}
+
+static const struct spi_master spi_master_dediprog = {
+ .type = SPI_CONTROLLER_DEDIPROG,
+ .max_data_read = 16, /* 18 seems to work fine as well, but 19 times out sometimes with FW 5.15. */
+ .max_data_write = 16,
+ .command = dediprog_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = dediprog_spi_read,
+ .write_256 = dediprog_spi_write_256,
+ .write_aai = dediprog_spi_write_aai,
+};
+
+static int dediprog_shutdown(void *data)
+{
+ dediprog_firmwareversion = FIRMWARE_VERSION(0, 0, 0);
+ dediprog_devicetype = DEV_UNKNOWN;
+
+ /* URB 28. Command Set SPI Voltage to 0. */
+ if (dediprog_set_spi_voltage(0x0))
+ return 1;
+
+ if (libusb_release_interface(dediprog_handle, 0)) {
+ msg_perr("Could not release USB interface!\n");
+ return 1;
+ }
+ libusb_close(dediprog_handle);
+ libusb_exit(usb_ctx);
+
+ return 0;
+}
+
+int dediprog_init(void)
+{
+ char *voltage, *device, *spispeed, *target_str;
+ int spispeed_idx = 1;
+ int millivolt = 3500;
+ long usedevice = 0;
+ long target = 1;
+ int i, ret;
+
+ spispeed = extract_programmer_param("spispeed");
+ if (spispeed) {
+ for (i = 0; spispeeds[i].name; ++i) {
+ if (!strcasecmp(spispeeds[i].name, spispeed)) {
+ spispeed_idx = i;
+ break;
+ }
+ }
+ if (!spispeeds[i].name) {
+ msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
+ free(spispeed);
+ return 1;
+ }
+ free(spispeed);
+ }
+
+ voltage = extract_programmer_param("voltage");
+ if (voltage) {
+ millivolt = parse_voltage(voltage);
+ free(voltage);
+ if (millivolt < 0)
+ return 1;
+ msg_pinfo("Setting voltage to %i mV\n", millivolt);
+ }
+
+ device = extract_programmer_param("device");
+ if (device) {
+ char *dev_suffix;
+ errno = 0;
+ usedevice = strtol(device, &dev_suffix, 10);
+ if (errno != 0 || device == dev_suffix) {
+ msg_perr("Error: Could not convert 'device'.\n");
+ free(device);
+ return 1;
+ }
+ if (usedevice < 0 || usedevice > UINT_MAX) {
+ msg_perr("Error: Value for 'device' is out of range.\n");
+ free(device);
+ return 1;
+ }
+ if (strlen(dev_suffix) > 0) {
+ msg_perr("Error: Garbage following 'device' value.\n");
+ free(device);
+ return 1;
+ }
+ msg_pinfo("Using device %li.\n", usedevice);
+ }
+ free(device);
+
+ target_str = extract_programmer_param("target");
+ if (target_str) {
+ char *target_suffix;
+ errno = 0;
+ target = strtol(target_str, &target_suffix, 10);
+ if (errno != 0 || target_str == target_suffix) {
+ msg_perr("Error: Could not convert 'target'.\n");
+ free(target_str);
+ return 1;
+ }
+ if (target < 1 || target > 2) {
+ msg_perr("Error: Value for 'target' is out of range.\n");
+ free(target_str);
+ return 1;
+ }
+ if (strlen(target_suffix) > 0) {
+ msg_perr("Error: Garbage following 'target' value.\n");
+ free(target_str);
+ return 1;
+ }
+ msg_pinfo("Using target %li.\n", target);
+ }
+ free(target_str);
+
+ /* Here comes the USB stuff. */
+ libusb_init(&usb_ctx);
+ if (!usb_ctx) {
+ msg_perr("Could not initialize libusb!\n");
+ return 1;
+ }
+
+ const uint16_t vid = devs_dediprog[0].vendor_id;
+ const uint16_t pid = devs_dediprog[0].device_id;
+ dediprog_handle = get_device_by_vid_pid_number(vid, pid, (unsigned int) usedevice);
+ if (!dediprog_handle) {
+ msg_perr("Could not find a Dediprog programmer on USB.\n");
+ libusb_exit(usb_ctx);
+ return 1;
+ }
+ ret = libusb_set_configuration(dediprog_handle, 1);
+ if (ret != 0) {
+ msg_perr("Could not set USB device configuration: %i %s\n", ret, libusb_error_name(ret));
+ libusb_close(dediprog_handle);
+ libusb_exit(usb_ctx);
+ return 1;
+ }
+ ret = libusb_claim_interface(dediprog_handle, 0);
+ if (ret < 0) {
+ msg_perr("Could not claim USB device interface %i: %i %s\n", 0, ret, libusb_error_name(ret));
+ libusb_close(dediprog_handle);
+ libusb_exit(usb_ctx);
+ return 1;
+ }
+
+ if (register_shutdown(dediprog_shutdown, NULL))
+ return 1;
+
+ /* Try reading the devicestring. If that fails and the device is old (FW < 6.0.0, which we can not know)
+ * then we need to try the "set voltage" command and then attempt to read the devicestring again. */
+ if (dediprog_check_devicestring()) {
+ if (dediprog_set_voltage())
+ return 1;
+ if (dediprog_check_devicestring())
+ return 1;
+ }
+
+ /* SF100 only has 1 endpoint for in/out, SF600 uses two separate endpoints instead. */
+ dediprog_in_endpoint = 2;
+ if (dediprog_devicetype == DEV_SF100)
+ dediprog_out_endpoint = 2;
+ else
+ dediprog_out_endpoint = 1;
+
+ /* Set all possible LEDs as soon as possible to indicate activity.
+ * Because knowing the firmware version is required to set the LEDs correctly we need to this after
+ * dediprog_check_devicestring() has queried the device and set dediprog_firmwareversion. */
+ dediprog_set_leds(LED_ALL);
+
+ /* Select target/socket, frequency and VCC. */
+ if (set_target_flash(FLASH_TYPE_APPLICATION_FLASH_1) ||
+ dediprog_set_spi_speed(spispeed_idx) ||
+ dediprog_set_spi_voltage(millivolt)) {
+ dediprog_set_leds(LED_ERROR);
+ return 1;
+ }
+
+ if (dediprog_standalone_mode())
+ return 1;
+
+ if (register_spi_master(&spi_master_dediprog) || dediprog_set_leds(LED_NONE))
+ return 1;
+
+ return 0;
+}
diff --git a/dmi.c b/dmi.c
new file mode 100644
index 0000000..729cdb1
--- /dev/null
+++ b/dmi.c
@@ -0,0 +1,484 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
+ * Copyright (C) 2002-2010 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2009,2010 Michael Karcher
+ * Copyright (C) 2011-2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* strnlen is in POSIX but was a GNU extension up to glibc 2.10 */
+#if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 10) || __GLIBC__ < 2
+#define _GNU_SOURCE
+#else
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "flash.h"
+#include "programmer.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/* Enable SMBIOS decoding. Currently legacy DMI decoding is enough. */
+#define SM_SUPPORT 0
+
+/* Strings longer than 4096 in DMI are just insane. */
+#define DMI_MAX_ANSWER_LEN 4096
+
+int has_dmi_support = 0;
+
+static struct {
+ const char *const keyword;
+ const uint8_t type;
+ const uint8_t offset;
+ char *value;
+} dmi_strings[] = {
+ { "system-manufacturer", 1, 0x04, NULL },
+ { "system-product-name", 1, 0x05, NULL },
+ { "system-version", 1, 0x06, NULL },
+ { "baseboard-manufacturer", 2, 0x04, NULL },
+ { "baseboard-product-name", 2, 0x05, NULL },
+ { "baseboard-version", 2, 0x06, NULL },
+};
+
+/* This list is used to identify supposed laptops. The is_laptop field has the
+ * following meaning:
+ * - 0: in all likelihood not a laptop
+ * - 1: in all likelihood a laptop
+ * - 2: chassis-type is not specific enough
+ * A full list of chassis types can be found in the System Management BIOS
+ * (SMBIOS) Reference Specification 2.7.0 section 7.4.1 "Chassis Types" at
+ * http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.0.pdf
+ * The types below are the most common ones.
+ */
+static const struct {
+ uint8_t type;
+ uint8_t is_laptop;
+ char *name;
+} dmi_chassis_types[] = {
+ {0x01, 2, "Other"},
+ {0x02, 2, "Unknown"},
+ {0x03, 0, "Desktop"},
+ {0x04, 0, "Low Profile Desktop"},
+ {0x06, 0, "Mini Tower"},
+ {0x07, 0, "Tower"},
+ {0x08, 1, "Portable"},
+ {0x09, 1, "Laptop"},
+ {0x0a, 1, "Notebook"},
+ {0x0b, 1, "Hand Held"},
+ {0x0e, 1, "Sub Notebook"},
+ {0x11, 0, "Main Server Chassis"},
+ {0x17, 0, "Rack Mount Chassis"},
+ {0x18, 0, "Sealed-case PC"}, /* used by Supermicro (X8SIE) */
+ {0x19, 0, "Multi-system"}, /* used by Supermicro (X7DWT) */
+};
+
+#if CONFIG_INTERNAL_DMI == 1
+static bool dmi_checksum(const uint8_t * const buf, size_t len)
+{
+ uint8_t sum = 0;
+ size_t a;
+
+ for (a = 0; a < len; a++)
+ sum += buf[a];
+ return (sum == 0);
+}
+
+/** Retrieve a DMI string.
+ *
+ * See SMBIOS spec. section 6.1.3 "Text strings".
+ * The table will be unmapped ASAP, hence return a duplicated & sanitized string that needs to be freed later.
+ *
+ * \param buf the buffer to search through (usually appended directly to a DMI structure)
+ * \param string_id index of the string to look for
+ * \param limit pointer to the first byte beyond \em buf
+ */
+static char *dmi_string(const char *buf, uint8_t string_id, const char *limit)
+{
+ size_t i, len;
+
+ if (string_id == 0)
+ return strdup("Not Specified");
+
+ while (string_id > 1 && string_id--) {
+ if (buf >= limit) {
+ msg_perr("DMI table is broken (string portion out of bounds)!\n");
+ return strdup("<OUT OF BOUNDS>");
+ }
+ buf += strnlen(buf, limit - buf) + 1;
+ }
+
+ if (!*buf) /* as long as the current byte we're on isn't null */
+ return strdup("<BAD INDEX>");
+
+ len = strnlen(buf, limit - buf);
+ char *newbuf = malloc(len + 1);
+ if (newbuf == NULL) {
+ msg_perr("Out of memory!\n");
+ return NULL;
+ }
+
+ /* fix junk bytes in the string */
+ for (i = 0; i < len && buf[i] != '\0'; i++) {
+ if (isprint((unsigned char)buf[i]))
+ newbuf[i] = buf[i];
+ else
+ newbuf[i] = ' ';
+ }
+ newbuf[i] = '\0';
+
+ return newbuf;
+}
+
+static void dmi_chassis_type(uint8_t code)
+{
+ int i;
+ code &= 0x7f; /* bits 6:0 are chassis type, 7th bit is the lock bit */
+ is_laptop = 2;
+ for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) {
+ if (code == dmi_chassis_types[i].type) {
+ msg_pdbg("DMI string chassis-type: \"%s\"\n", dmi_chassis_types[i].name);
+ is_laptop = dmi_chassis_types[i].is_laptop;
+ break;
+ }
+ }
+}
+
+static void dmi_table(uint32_t base, uint16_t len, uint16_t num)
+{
+ int i = 0, j = 0;
+
+ uint8_t *dmi_table_mem = physmap_ro("DMI Table", base, len);
+ if (dmi_table_mem == NULL) {
+ msg_perr("Unable to access DMI Table\n");
+ return;
+ }
+
+ uint8_t *data = dmi_table_mem;
+ uint8_t *limit = dmi_table_mem + len;
+
+ /* SMBIOS structure header is always 4 B long and contains:
+ * - uint8_t type; // see dmi_chassis_types's type
+ * - uint8_t length; // data section w/ header w/o strings
+ * - uint16_t handle;
+ */
+ while (i < num && data + 4 < limit) {
+ /* - If a short entry is found (less than 4 bytes), not only it
+ * is invalid, but we cannot reliably locate the next entry.
+ * - If the length value indicates that this structure spreads
+ * across the table border, something is fishy too.
+ * Better stop at this point, and let the user know his/her
+ * table is broken.
+ */
+ if (data[1] < 4 || data + data[1] >= limit) {
+ msg_perr("DMI table is broken (bogus header)!\n");
+ break;
+ }
+
+ if(data[0] == 3) {
+ if (data + 5 < limit)
+ dmi_chassis_type(data[5]);
+ else /* the table is broken, but laptop detection is optional, hence continue. */
+ msg_pwarn("DMI table is broken (chassis_type out of bounds)!\n");
+ } else
+ for (j = 0; j < ARRAY_SIZE(dmi_strings); j++) {
+ uint8_t offset = dmi_strings[j].offset;
+ uint8_t type = dmi_strings[j].type;
+
+ if (data[0] != type)
+ continue;
+
+ if (data[1] <= offset || data + offset >= limit) {
+ msg_perr("DMI table is broken (offset out of bounds)!\n");
+ goto out;
+ }
+
+ dmi_strings[j].value = dmi_string((const char *)(data + data[1]), data[offset],
+ (const char *)limit);
+ }
+ /* Find next structure by skipping data and string sections */
+ data += data[1];
+ while (data + 1 <= limit) {
+ if (data[0] == 0 && data[1] == 0)
+ break;
+ data++;
+ }
+ data += 2;
+ i++;
+ }
+out:
+ physunmap(dmi_table_mem, len);
+}
+
+#if SM_SUPPORT
+static int smbios_decode(uint8_t *buf)
+{
+ /* TODO: other checks mentioned in the conformance guidelines? */
+ if (!dmi_checksum(buf, buf[0x05]) ||
+ (memcmp(buf + 0x10, "_DMI_", 5) != 0) ||
+ !dmi_checksum(buf + 0x10, 0x0F))
+ return 0;
+
+ dmi_table(mmio_readl(buf + 0x18), mmio_readw(buf + 0x16), mmio_readw(buf + 0x1C));
+
+ return 1;
+}
+#endif
+
+static int legacy_decode(uint8_t *buf)
+{
+ if (!dmi_checksum(buf, 0x0F))
+ return 1;
+
+ dmi_table(mmio_readl(buf + 0x08), mmio_readw(buf + 0x06), mmio_readw(buf + 0x0C));
+
+ return 0;
+}
+
+int dmi_fill(void)
+{
+ size_t fp;
+ uint8_t *dmi_mem;
+ int ret = 1;
+
+ msg_pdbg("Using Internal DMI decoder.\n");
+ /* There are two ways specified to gain access to the SMBIOS table:
+ * - EFI's configuration table contains a pointer to the SMBIOS table. On linux it can be obtained from
+ * sysfs. EFI's SMBIOS GUID is: {0xeb9d2d31,0x2d88,0x11d3,0x9a,0x16,0x0,0x90,0x27,0x3f,0xc1,0x4d}
+ * - Scanning physical memory address range 0x000F0000h to 0x000FFFFF for the anchor-string(s). */
+ dmi_mem = physmap_ro("DMI", 0xF0000, 0x10000);
+ if (dmi_mem == ERROR_PTR)
+ return ret;
+
+ for (fp = 0; fp <= 0xFFF0; fp += 16) {
+#if SM_SUPPORT
+ if (memcmp(dmi_mem + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
+ if (smbios_decode(dmi_mem + fp)) // FIXME: length check
+ goto out;
+ } else
+#endif
+ if (memcmp(dmi_mem + fp, "_DMI_", 5) == 0)
+ if (legacy_decode(dmi_mem + fp) == 0) {
+ ret = 0;
+ goto out;
+ }
+ }
+ msg_pinfo("No DMI table found.\n");
+out:
+ physunmap(dmi_mem, 0x10000);
+ return ret;
+}
+
+#else /* CONFIG_INTERNAL_DMI */
+
+#define DMI_COMMAND_LEN_MAX 300
+static const char *dmidecode_command = "dmidecode";
+
+static char *get_dmi_string(const char *string_name)
+{
+ FILE *dmidecode_pipe;
+ char *result;
+ char answerbuf[DMI_MAX_ANSWER_LEN];
+ char commandline[DMI_COMMAND_LEN_MAX];
+
+ snprintf(commandline, sizeof(commandline),
+ "%s -s %s", dmidecode_command, string_name);
+ dmidecode_pipe = popen(commandline, "r");
+ if (!dmidecode_pipe) {
+ msg_perr("Opening DMI pipe failed!\n");
+ return NULL;
+ }
+
+ /* Kill lines starting with '#', as recent dmidecode versions
+ * have the quirk to emit a "# SMBIOS implementations newer..."
+ * message even on "-s" if the SMBIOS declares a
+ * newer-than-supported version number, while it *should* only print
+ * the requested string.
+ */
+ do {
+ if (!fgets(answerbuf, DMI_MAX_ANSWER_LEN, dmidecode_pipe)) {
+ if (ferror(dmidecode_pipe)) {
+ msg_perr("DMI pipe read error\n");
+ pclose(dmidecode_pipe);
+ return NULL;
+ } else {
+ answerbuf[0] = 0; /* Hit EOF */
+ }
+ }
+ } while (answerbuf[0] == '#');
+
+ /* Discard all output exceeding DMI_MAX_ANSWER_LEN to prevent deadlock on pclose. */
+ while (!feof(dmidecode_pipe))
+ getc(dmidecode_pipe);
+ if (pclose(dmidecode_pipe) != 0) {
+ msg_pwarn("dmidecode execution unsuccessful - continuing without DMI info\n");
+ return NULL;
+ }
+
+ /* Chomp trailing newline. */
+ if (answerbuf[0] != 0 && answerbuf[strlen(answerbuf) - 1] == '\n')
+ answerbuf[strlen(answerbuf) - 1] = 0;
+
+ result = strdup(answerbuf);
+ if (result == NULL)
+ msg_pwarn("Warning: Out of memory - DMI support fails");
+
+ return result;
+}
+
+int dmi_fill(void)
+{
+ int i;
+ char *chassis_type;
+
+ msg_pdbg("Using External DMI decoder.\n");
+ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) {
+ dmi_strings[i].value = get_dmi_string(dmi_strings[i].keyword);
+ if (dmi_strings[i].value == NULL)
+ return 1;
+ }
+
+ chassis_type = get_dmi_string("chassis-type");
+ if (chassis_type == NULL)
+ return 0; /* chassis-type handling is optional anyway */
+
+ msg_pdbg("DMI string chassis-type: \"%s\"\n", chassis_type);
+ is_laptop = 2;
+ for (i = 0; i < ARRAY_SIZE(dmi_chassis_types); i++) {
+ if (strcasecmp(chassis_type, dmi_chassis_types[i].name) == 0) {
+ is_laptop = dmi_chassis_types[i].is_laptop;
+ break;
+ }
+ }
+ free(chassis_type);
+ return 0;
+}
+
+#endif /* CONFIG_INTERNAL_DMI */
+
+static int dmi_shutdown(void *data)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) {
+ free(dmi_strings[i].value);
+ dmi_strings[i].value = NULL;
+ }
+ return 0;
+}
+
+void dmi_init(void)
+{
+ /* Register shutdown function before we allocate anything. */
+ if (register_shutdown(dmi_shutdown, NULL)) {
+ msg_pwarn("Warning: Could not register DMI shutdown function - continuing without DMI info.\n");
+ return;
+ }
+
+ /* dmi_fill fills the dmi_strings array, and if possible sets the global is_laptop variable. */
+ if (dmi_fill() != 0)
+ return;
+
+ switch (is_laptop) {
+ case 1:
+ msg_pdbg("Laptop detected via DMI.\n");
+ break;
+ case 2:
+ msg_pdbg("DMI chassis-type is not specific enough.\n");
+ break;
+ }
+
+ has_dmi_support = 1;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) {
+ msg_pdbg("DMI string %s: \"%s\"\n", dmi_strings[i].keyword,
+ (dmi_strings[i].value == NULL) ? "" : dmi_strings[i].value);
+ }
+}
+
+/**
+ * Does an substring/prefix/postfix/whole-string match.
+ *
+ * The pattern is matched as-is. The only metacharacters supported are '^'
+ * at the beginning and '$' at the end. So you can look for "^prefix",
+ * "suffix$", "substring" or "^complete string$".
+ *
+ * @param value The non-NULL string to check.
+ * @param pattern The non-NULL pattern.
+ * @return Nonzero if pattern matches.
+ */
+static int dmi_compare(const char *value, const char *pattern)
+{
+ int anchored = 0;
+ int patternlen;
+
+ msg_pspew("matching %s against %s\n", value, pattern);
+ /* The empty string is part of all strings! */
+ if (pattern[0] == 0)
+ return 1;
+
+ if (pattern[0] == '^') {
+ anchored = 1;
+ pattern++;
+ }
+
+ patternlen = strlen(pattern);
+ if (pattern[patternlen - 1] == '$') {
+ int valuelen = strlen(value);
+ patternlen--;
+ if (patternlen > valuelen)
+ return 0;
+
+ /* full string match: require same length */
+ if (anchored && (valuelen != patternlen))
+ return 0;
+
+ /* start character to make ends match */
+ value += valuelen - patternlen;
+ anchored = 1;
+ }
+
+ if (anchored)
+ return strncmp(value, pattern, patternlen) == 0;
+ else
+ return strstr(value, pattern) != NULL;
+}
+
+int dmi_match(const char *pattern)
+{
+ int i;
+
+ if (!has_dmi_support)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(dmi_strings); i++) {
+ if (dmi_strings[i].value == NULL)
+ continue;
+
+ if (dmi_compare(dmi_strings[i].value, pattern))
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/drkaiser.c b/drkaiser.c
new file mode 100644
index 0000000..75cc085
--- /dev/null
+++ b/drkaiser.c
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Joerg Fischer <turboj@web.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_DRKAISER 0x1803
+
+#define PCI_MAGIC_DRKAISER_ADDR 0x50
+#define PCI_MAGIC_DRKAISER_VALUE 0xa971
+
+#define DRKAISER_MEMMAP_SIZE (1024 * 128)
+
+/* Mask to restrict flash accesses to the 128kB memory window. */
+#define DRKAISER_MEMMAP_MASK ((1 << 17) - 1)
+
+const struct dev_entry drkaiser_pcidev[] = {
+ {0x1803, 0x5057, OK, "Dr. Kaiser", "PC-Waechter (Actel FPGA)"},
+
+ {0},
+};
+
+static uint8_t *drkaiser_bar;
+
+static void drkaiser_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t drkaiser_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_drkaiser = {
+ .chip_readb = drkaiser_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = drkaiser_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int drkaiser_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t addr;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(drkaiser_pcidev, PCI_BASE_ADDRESS_2);
+ if (!dev)
+ return 1;
+
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2);
+ if (!addr)
+ return 1;
+
+ /* Write magic register to enable flash write. */
+ rpci_write_word(dev, PCI_MAGIC_DRKAISER_ADDR, PCI_MAGIC_DRKAISER_VALUE);
+
+ /* Map 128kB flash memory window. */
+ drkaiser_bar = rphysmap("Dr. Kaiser PC-Waechter flash memory", addr, DRKAISER_MEMMAP_SIZE);
+ if (drkaiser_bar == ERROR_PTR)
+ return 1;
+
+ max_rom_decode.parallel = 128 * 1024;
+ register_par_master(&par_master_drkaiser, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void drkaiser_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ pci_mmio_writeb(val, drkaiser_bar + (addr & DRKAISER_MEMMAP_MASK));
+}
+
+static uint8_t drkaiser_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return pci_mmio_readb(drkaiser_bar + (addr & DRKAISER_MEMMAP_MASK));
+}
diff --git a/dummyflasher.c b/dummyflasher.c
new file mode 100644
index 0000000..f171128
--- /dev/null
+++ b/dummyflasher.c
@@ -0,0 +1,833 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+
+/* Remove the #define below if you don't want SPI flash chip emulation. */
+#define EMULATE_SPI_CHIP 1
+
+#if EMULATE_SPI_CHIP
+#define EMULATE_CHIP 1
+#include "spi.h"
+#endif
+
+#if EMULATE_CHIP
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#if EMULATE_CHIP
+static uint8_t *flashchip_contents = NULL;
+enum emu_chip {
+ EMULATE_NONE,
+ EMULATE_ST_M25P10_RES,
+ EMULATE_SST_SST25VF040_REMS,
+ EMULATE_SST_SST25VF032B,
+ EMULATE_MACRONIX_MX25L6436,
+};
+static enum emu_chip emu_chip = EMULATE_NONE;
+static char *emu_persistent_image = NULL;
+static unsigned int emu_chip_size = 0;
+#if EMULATE_SPI_CHIP
+static unsigned int emu_max_byteprogram_size = 0;
+static unsigned int emu_max_aai_size = 0;
+static unsigned int emu_jedec_se_size = 0;
+static unsigned int emu_jedec_be_52_size = 0;
+static unsigned int emu_jedec_be_d8_size = 0;
+static unsigned int emu_jedec_ce_60_size = 0;
+static unsigned int emu_jedec_ce_c7_size = 0;
+unsigned char spi_blacklist[256];
+unsigned char spi_ignorelist[256];
+int spi_blacklist_size = 0;
+int spi_ignorelist_size = 0;
+static uint8_t emu_status = 0;
+
+/* A legit complete SFDP table based on the MX25L6436E (rev. 1.8) datasheet. */
+static const uint8_t sfdp_table[] = {
+ 0x53, 0x46, 0x44, 0x50, // @0x00: SFDP signature
+ 0x00, 0x01, 0x01, 0xFF, // @0x04: revision 1.0, 2 headers
+ 0x00, 0x00, 0x01, 0x09, // @0x08: JEDEC SFDP header rev. 1.0, 9 DW long
+ 0x1C, 0x00, 0x00, 0xFF, // @0x0C: PTP0 = 0x1C (instead of 0x30)
+ 0xC2, 0x00, 0x01, 0x04, // @0x10: Macronix header rev. 1.0, 4 DW long
+ 0x48, 0x00, 0x00, 0xFF, // @0x14: PTP1 = 0x48 (instead of 0x60)
+ 0xFF, 0xFF, 0xFF, 0xFF, // @0x18: hole.
+ 0xE5, 0x20, 0xC9, 0xFF, // @0x1C: SFDP parameter table start
+ 0xFF, 0xFF, 0xFF, 0x03, // @0x20
+ 0x00, 0xFF, 0x08, 0x6B, // @0x24
+ 0x08, 0x3B, 0x00, 0xFF, // @0x28
+ 0xEE, 0xFF, 0xFF, 0xFF, // @0x2C
+ 0xFF, 0xFF, 0x00, 0x00, // @0x30
+ 0xFF, 0xFF, 0x00, 0xFF, // @0x34
+ 0x0C, 0x20, 0x0F, 0x52, // @0x38
+ 0x10, 0xD8, 0x00, 0xFF, // @0x3C: SFDP parameter table end
+ 0xFF, 0xFF, 0xFF, 0xFF, // @0x40: hole.
+ 0xFF, 0xFF, 0xFF, 0xFF, // @0x44: hole.
+ 0x00, 0x36, 0x00, 0x27, // @0x48: Macronix parameter table start
+ 0xF4, 0x4F, 0xFF, 0xFF, // @0x4C
+ 0xD9, 0xC8, 0xFF, 0xFF, // @0x50
+ 0xFF, 0xFF, 0xFF, 0xFF, // @0x54: Macronix parameter table end
+};
+
+#endif
+#endif
+
+static unsigned int spi_write_256_chunksize = 256;
+
+static int dummy_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+static int dummy_spi_write_256(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len);
+static void dummy_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static void dummy_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr);
+static void dummy_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr);
+static void dummy_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len);
+static uint8_t dummy_chip_readb(const struct flashctx *flash, const chipaddr addr);
+static uint16_t dummy_chip_readw(const struct flashctx *flash, const chipaddr addr);
+static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr addr);
+static void dummy_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len);
+
+static const struct spi_master spi_master_dummyflasher = {
+ .type = SPI_CONTROLLER_DUMMY,
+ .max_data_read = MAX_DATA_READ_UNLIMITED,
+ .max_data_write = MAX_DATA_UNSPECIFIED,
+ .command = dummy_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = dummy_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static const struct par_master par_master_dummy = {
+ .chip_readb = dummy_chip_readb,
+ .chip_readw = dummy_chip_readw,
+ .chip_readl = dummy_chip_readl,
+ .chip_readn = dummy_chip_readn,
+ .chip_writeb = dummy_chip_writeb,
+ .chip_writew = dummy_chip_writew,
+ .chip_writel = dummy_chip_writel,
+ .chip_writen = dummy_chip_writen,
+};
+
+enum chipbustype dummy_buses_supported = BUS_NONE;
+
+static int dummy_shutdown(void *data)
+{
+ msg_pspew("%s\n", __func__);
+#if EMULATE_CHIP
+ if (emu_chip != EMULATE_NONE) {
+ if (emu_persistent_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_image);
+ write_buf_to_file(flashchip_contents, emu_chip_size, emu_persistent_image);
+ free(emu_persistent_image);
+ emu_persistent_image = NULL;
+ }
+ free(flashchip_contents);
+ }
+#endif
+ return 0;
+}
+
+int dummy_init(void)
+{
+ char *bustext = NULL;
+ char *tmp = NULL;
+ int i;
+#if EMULATE_SPI_CHIP
+ char *status = NULL;
+#endif
+#if EMULATE_CHIP
+ struct stat image_stat;
+#endif
+
+ msg_pspew("%s\n", __func__);
+
+ bustext = extract_programmer_param("bus");
+ msg_pdbg("Requested buses are: %s\n", bustext ? bustext : "default");
+ if (!bustext)
+ bustext = strdup("parallel+lpc+fwh+spi");
+ /* Convert the parameters to lowercase. */
+ tolower_string(bustext);
+
+ dummy_buses_supported = BUS_NONE;
+ if (strstr(bustext, "parallel")) {
+ dummy_buses_supported |= BUS_PARALLEL;
+ msg_pdbg("Enabling support for %s flash.\n", "parallel");
+ }
+ if (strstr(bustext, "lpc")) {
+ dummy_buses_supported |= BUS_LPC;
+ msg_pdbg("Enabling support for %s flash.\n", "LPC");
+ }
+ if (strstr(bustext, "fwh")) {
+ dummy_buses_supported |= BUS_FWH;
+ msg_pdbg("Enabling support for %s flash.\n", "FWH");
+ }
+ if (strstr(bustext, "spi")) {
+ dummy_buses_supported |= BUS_SPI;
+ msg_pdbg("Enabling support for %s flash.\n", "SPI");
+ }
+ if (dummy_buses_supported == BUS_NONE)
+ msg_pdbg("Support for all flash bus types disabled.\n");
+ free(bustext);
+
+ tmp = extract_programmer_param("spi_write_256_chunksize");
+ if (tmp) {
+ spi_write_256_chunksize = atoi(tmp);
+ free(tmp);
+ if (spi_write_256_chunksize < 1) {
+ msg_perr("invalid spi_write_256_chunksize\n");
+ return 1;
+ }
+ }
+
+ tmp = extract_programmer_param("spi_blacklist");
+ if (tmp) {
+ i = strlen(tmp);
+ if (!strncmp(tmp, "0x", 2)) {
+ i -= 2;
+ memmove(tmp, tmp + 2, i + 1);
+ }
+ if ((i > 512) || (i % 2)) {
+ msg_perr("Invalid SPI command blacklist length\n");
+ free(tmp);
+ return 1;
+ }
+ spi_blacklist_size = i / 2;
+ for (i = 0; i < spi_blacklist_size * 2; i++) {
+ if (!isxdigit((unsigned char)tmp[i])) {
+ msg_perr("Invalid char \"%c\" in SPI command "
+ "blacklist\n", tmp[i]);
+ free(tmp);
+ return 1;
+ }
+ }
+ for (i = 0; i < spi_blacklist_size; i++) {
+ unsigned int tmp2;
+ /* SCNx8 is apparently not supported by MSVC (and thus
+ * MinGW), so work around it with an extra variable
+ */
+ sscanf(tmp + i * 2, "%2x", &tmp2);
+ spi_blacklist[i] = (uint8_t)tmp2;
+ }
+ msg_pdbg("SPI blacklist is ");
+ for (i = 0; i < spi_blacklist_size; i++)
+ msg_pdbg("%02x ", spi_blacklist[i]);
+ msg_pdbg(", size %i\n", spi_blacklist_size);
+ }
+ free(tmp);
+
+ tmp = extract_programmer_param("spi_ignorelist");
+ if (tmp) {
+ i = strlen(tmp);
+ if (!strncmp(tmp, "0x", 2)) {
+ i -= 2;
+ memmove(tmp, tmp + 2, i + 1);
+ }
+ if ((i > 512) || (i % 2)) {
+ msg_perr("Invalid SPI command ignorelist length\n");
+ free(tmp);
+ return 1;
+ }
+ spi_ignorelist_size = i / 2;
+ for (i = 0; i < spi_ignorelist_size * 2; i++) {
+ if (!isxdigit((unsigned char)tmp[i])) {
+ msg_perr("Invalid char \"%c\" in SPI command "
+ "ignorelist\n", tmp[i]);
+ free(tmp);
+ return 1;
+ }
+ }
+ for (i = 0; i < spi_ignorelist_size; i++) {
+ unsigned int tmp2;
+ /* SCNx8 is apparently not supported by MSVC (and thus
+ * MinGW), so work around it with an extra variable
+ */
+ sscanf(tmp + i * 2, "%2x", &tmp2);
+ spi_ignorelist[i] = (uint8_t)tmp2;
+ }
+ msg_pdbg("SPI ignorelist is ");
+ for (i = 0; i < spi_ignorelist_size; i++)
+ msg_pdbg("%02x ", spi_ignorelist[i]);
+ msg_pdbg(", size %i\n", spi_ignorelist_size);
+ }
+ free(tmp);
+
+#if EMULATE_CHIP
+ tmp = extract_programmer_param("emulate");
+ if (!tmp) {
+ msg_pdbg("Not emulating any flash chip.\n");
+ /* Nothing else to do. */
+ goto dummy_init_out;
+ }
+#if EMULATE_SPI_CHIP
+ if (!strcmp(tmp, "M25P10.RES")) {
+ emu_chip = EMULATE_ST_M25P10_RES;
+ emu_chip_size = 128 * 1024;
+ emu_max_byteprogram_size = 128;
+ emu_max_aai_size = 0;
+ emu_jedec_se_size = 0;
+ emu_jedec_be_52_size = 0;
+ emu_jedec_be_d8_size = 32 * 1024;
+ emu_jedec_ce_60_size = 0;
+ emu_jedec_ce_c7_size = emu_chip_size;
+ msg_pdbg("Emulating ST M25P10.RES SPI flash chip (RES, page "
+ "write)\n");
+ }
+ if (!strcmp(tmp, "SST25VF040.REMS")) {
+ emu_chip = EMULATE_SST_SST25VF040_REMS;
+ emu_chip_size = 512 * 1024;
+ emu_max_byteprogram_size = 1;
+ emu_max_aai_size = 0;
+ emu_jedec_se_size = 4 * 1024;
+ emu_jedec_be_52_size = 32 * 1024;
+ emu_jedec_be_d8_size = 0;
+ emu_jedec_ce_60_size = emu_chip_size;
+ emu_jedec_ce_c7_size = 0;
+ msg_pdbg("Emulating SST SST25VF040.REMS SPI flash chip (REMS, "
+ "byte write)\n");
+ }
+ if (!strcmp(tmp, "SST25VF032B")) {
+ emu_chip = EMULATE_SST_SST25VF032B;
+ emu_chip_size = 4 * 1024 * 1024;
+ emu_max_byteprogram_size = 1;
+ emu_max_aai_size = 2;
+ emu_jedec_se_size = 4 * 1024;
+ emu_jedec_be_52_size = 32 * 1024;
+ emu_jedec_be_d8_size = 64 * 1024;
+ emu_jedec_ce_60_size = emu_chip_size;
+ emu_jedec_ce_c7_size = emu_chip_size;
+ msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI "
+ "write)\n");
+ }
+ if (!strcmp(tmp, "MX25L6436")) {
+ emu_chip = EMULATE_MACRONIX_MX25L6436;
+ emu_chip_size = 8 * 1024 * 1024;
+ emu_max_byteprogram_size = 256;
+ emu_max_aai_size = 0;
+ emu_jedec_se_size = 4 * 1024;
+ emu_jedec_be_52_size = 32 * 1024;
+ emu_jedec_be_d8_size = 64 * 1024;
+ emu_jedec_ce_60_size = emu_chip_size;
+ emu_jedec_ce_c7_size = emu_chip_size;
+ msg_pdbg("Emulating Macronix MX25L6436 SPI flash chip (RDID, "
+ "SFDP)\n");
+ }
+#endif
+ if (emu_chip == EMULATE_NONE) {
+ msg_perr("Invalid chip specified for emulation: %s\n", tmp);
+ free(tmp);
+ return 1;
+ }
+ free(tmp);
+ flashchip_contents = malloc(emu_chip_size);
+ if (!flashchip_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+
+#ifdef EMULATE_SPI_CHIP
+ status = extract_programmer_param("spi_status");
+ if (status) {
+ char *endptr;
+ errno = 0;
+ emu_status = strtoul(status, &endptr, 0);
+ free(status);
+ if (errno != 0 || status == endptr) {
+ msg_perr("Error: initial status register specified, "
+ "but the value could not be converted.\n");
+ return 1;
+ }
+ msg_pdbg("Initial status register is set to 0x%02x.\n",
+ emu_status);
+ }
+#endif
+
+ msg_pdbg("Filling fake flash chip with 0xff, size %i\n", emu_chip_size);
+ memset(flashchip_contents, 0xff, emu_chip_size);
+
+ /* Will be freed by shutdown function if necessary. */
+ emu_persistent_image = extract_programmer_param("image");
+ if (!emu_persistent_image) {
+ /* Nothing else to do. */
+ goto dummy_init_out;
+ }
+ /* We will silently (in default verbosity) ignore the file if it does not exist (yet) or the size does
+ * not match the emulated chip. */
+ if (!stat(emu_persistent_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B ",
+ emu_persistent_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == emu_chip_size) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_image);
+ read_buf_from_file(flashchip_contents, emu_chip_size,
+ emu_persistent_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+#endif
+
+dummy_init_out:
+ if (register_shutdown(dummy_shutdown, NULL)) {
+ free(flashchip_contents);
+ return 1;
+ }
+ if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH))
+ register_par_master(&par_master_dummy,
+ dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH));
+ if (dummy_buses_supported & BUS_SPI)
+ register_spi_master(&spi_master_dummyflasher);
+
+ return 0;
+}
+
+void *dummy_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ msg_pspew("%s: Mapping %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n",
+ __func__, descr, len, PRIxPTR_WIDTH, phys_addr);
+ return (void *)phys_addr;
+}
+
+void dummy_unmap(void *virt_addr, size_t len)
+{
+ msg_pspew("%s: Unmapping 0x%zx bytes at %p\n", __func__, len, virt_addr);
+}
+
+static void dummy_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%02x\n", __func__, addr, val);
+}
+
+static void dummy_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%04x\n", __func__, addr, val);
+}
+
+static void dummy_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%08x\n", __func__, addr, val);
+}
+
+static void dummy_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len)
+{
+ size_t i;
+ msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, writing data (hex):", __func__, addr, len);
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0)
+ msg_pspew("\n");
+ msg_pspew("%02x ", buf[i]);
+ }
+}
+
+static uint8_t dummy_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xff\n", __func__, addr);
+ return 0xff;
+}
+
+static uint16_t dummy_chip_readw(const struct flashctx *flash, const chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffff\n", __func__, addr);
+ return 0xffff;
+}
+
+static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr addr)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffffffff\n", __func__, addr);
+ return 0xffffffff;
+}
+
+static void dummy_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len)
+{
+ msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, returning array of 0xff\n", __func__, addr, len);
+ memset(buf, 0xff, len);
+ return;
+}
+
+#if EMULATE_SPI_CHIP
+static int emulate_spi_chip_response(unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ unsigned int offs, i, toread;
+ static int unsigned aai_offs;
+ const unsigned char sst25vf040_rems_response[2] = {0xbf, 0x44};
+ const unsigned char sst25vf032b_rems_response[2] = {0xbf, 0x4a};
+ const unsigned char mx25l6436_rems_response[2] = {0xc2, 0x16};
+
+ if (writecnt == 0) {
+ msg_perr("No command sent to the chip!\n");
+ return 1;
+ }
+ /* spi_blacklist has precedence over spi_ignorelist. */
+ for (i = 0; i < spi_blacklist_size; i++) {
+ if (writearr[0] == spi_blacklist[i]) {
+ msg_pdbg("Refusing blacklisted SPI command 0x%02x\n",
+ spi_blacklist[i]);
+ return SPI_INVALID_OPCODE;
+ }
+ }
+ for (i = 0; i < spi_ignorelist_size; i++) {
+ if (writearr[0] == spi_ignorelist[i]) {
+ msg_cdbg("Ignoring ignorelisted SPI command 0x%02x\n",
+ spi_ignorelist[i]);
+ /* Return success because the command does not fail,
+ * it is simply ignored.
+ */
+ return 0;
+ }
+ }
+
+ if (emu_max_aai_size && (emu_status & SPI_SR_AAI)) {
+ if (writearr[0] != JEDEC_AAI_WORD_PROGRAM &&
+ writearr[0] != JEDEC_WRDI &&
+ writearr[0] != JEDEC_RDSR) {
+ msg_perr("Forbidden opcode (0x%02x) attempted during "
+ "AAI sequence!\n", writearr[0]);
+ return 0;
+ }
+ }
+
+ switch (writearr[0]) {
+ case JEDEC_RES:
+ if (writecnt < JEDEC_RES_OUTSIZE)
+ break;
+ /* offs calculation is only needed for SST chips which treat RES like REMS. */
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ offs += writecnt - JEDEC_REMS_OUTSIZE;
+ switch (emu_chip) {
+ case EMULATE_ST_M25P10_RES:
+ if (readcnt > 0)
+ memset(readarr, 0x10, readcnt);
+ break;
+ case EMULATE_SST_SST25VF040_REMS:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = sst25vf040_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_SST_SST25VF032B:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = sst25vf032b_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_MACRONIX_MX25L6436:
+ if (readcnt > 0)
+ memset(readarr, 0x16, readcnt);
+ break;
+ default: /* ignore */
+ break;
+ }
+ break;
+ case JEDEC_REMS:
+ /* REMS response has wraparound and uses an address parameter. */
+ if (writecnt < JEDEC_REMS_OUTSIZE)
+ break;
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ offs += writecnt - JEDEC_REMS_OUTSIZE;
+ switch (emu_chip) {
+ case EMULATE_SST_SST25VF040_REMS:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = sst25vf040_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_SST_SST25VF032B:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = sst25vf032b_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_MACRONIX_MX25L6436:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = mx25l6436_rems_response[(offs + i) % 2];
+ break;
+ default: /* ignore */
+ break;
+ }
+ break;
+ case JEDEC_RDID:
+ switch (emu_chip) {
+ case EMULATE_SST_SST25VF032B:
+ if (readcnt > 0)
+ readarr[0] = 0xbf;
+ if (readcnt > 1)
+ readarr[1] = 0x25;
+ if (readcnt > 2)
+ readarr[2] = 0x4a;
+ break;
+ case EMULATE_MACRONIX_MX25L6436:
+ if (readcnt > 0)
+ readarr[0] = 0xc2;
+ if (readcnt > 1)
+ readarr[1] = 0x20;
+ if (readcnt > 2)
+ readarr[2] = 0x17;
+ break;
+ default: /* ignore */
+ break;
+ }
+ break;
+ case JEDEC_RDSR:
+ memset(readarr, emu_status, readcnt);
+ break;
+ /* FIXME: this should be chip-specific. */
+ case JEDEC_EWSR:
+ case JEDEC_WREN:
+ emu_status |= SPI_SR_WEL;
+ break;
+ case JEDEC_WRSR:
+ if (!(emu_status & SPI_SR_WEL)) {
+ msg_perr("WRSR attempted, but WEL is 0!\n");
+ break;
+ }
+ /* FIXME: add some reasonable simulation of the busy flag */
+ emu_status = writearr[1] & ~SPI_SR_WIP;
+ msg_pdbg2("WRSR wrote 0x%02x.\n", emu_status);
+ break;
+ case JEDEC_READ:
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ /* Truncate to emu_chip_size. */
+ offs %= emu_chip_size;
+ if (readcnt > 0)
+ memcpy(readarr, flashchip_contents + offs, readcnt);
+ break;
+ case JEDEC_BYTE_PROGRAM:
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ /* Truncate to emu_chip_size. */
+ offs %= emu_chip_size;
+ if (writecnt < 5) {
+ msg_perr("BYTE PROGRAM size too short!\n");
+ return 1;
+ }
+ if (writecnt - 4 > emu_max_byteprogram_size) {
+ msg_perr("Max BYTE PROGRAM size exceeded!\n");
+ return 1;
+ }
+ memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4);
+ break;
+ case JEDEC_AAI_WORD_PROGRAM:
+ if (!emu_max_aai_size)
+ break;
+ if (!(emu_status & SPI_SR_AAI)) {
+ if (writecnt < JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+ msg_perr("Initial AAI WORD PROGRAM size too "
+ "short!\n");
+ return 1;
+ }
+ if (writecnt > JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+ msg_perr("Initial AAI WORD PROGRAM size too "
+ "long!\n");
+ return 1;
+ }
+ emu_status |= SPI_SR_AAI;
+ aai_offs = writearr[1] << 16 | writearr[2] << 8 |
+ writearr[3];
+ /* Truncate to emu_chip_size. */
+ aai_offs %= emu_chip_size;
+ memcpy(flashchip_contents + aai_offs, writearr + 4, 2);
+ aai_offs += 2;
+ } else {
+ if (writecnt < JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) {
+ msg_perr("Continuation AAI WORD PROGRAM size "
+ "too short!\n");
+ return 1;
+ }
+ if (writecnt > JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) {
+ msg_perr("Continuation AAI WORD PROGRAM size "
+ "too long!\n");
+ return 1;
+ }
+ memcpy(flashchip_contents + aai_offs, writearr + 1, 2);
+ aai_offs += 2;
+ }
+ break;
+ case JEDEC_WRDI:
+ if (emu_max_aai_size)
+ emu_status &= ~SPI_SR_AAI;
+ break;
+ case JEDEC_SE:
+ if (!emu_jedec_se_size)
+ break;
+ if (writecnt != JEDEC_SE_OUTSIZE) {
+ msg_perr("SECTOR ERASE 0x20 outsize invalid!\n");
+ return 1;
+ }
+ if (readcnt != JEDEC_SE_INSIZE) {
+ msg_perr("SECTOR ERASE 0x20 insize invalid!\n");
+ return 1;
+ }
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ if (offs & (emu_jedec_se_size - 1))
+ msg_pdbg("Unaligned SECTOR ERASE 0x20: 0x%x\n", offs);
+ offs &= ~(emu_jedec_se_size - 1);
+ memset(flashchip_contents + offs, 0xff, emu_jedec_se_size);
+ break;
+ case JEDEC_BE_52:
+ if (!emu_jedec_be_52_size)
+ break;
+ if (writecnt != JEDEC_BE_52_OUTSIZE) {
+ msg_perr("BLOCK ERASE 0x52 outsize invalid!\n");
+ return 1;
+ }
+ if (readcnt != JEDEC_BE_52_INSIZE) {
+ msg_perr("BLOCK ERASE 0x52 insize invalid!\n");
+ return 1;
+ }
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ if (offs & (emu_jedec_be_52_size - 1))
+ msg_pdbg("Unaligned BLOCK ERASE 0x52: 0x%x\n", offs);
+ offs &= ~(emu_jedec_be_52_size - 1);
+ memset(flashchip_contents + offs, 0xff, emu_jedec_be_52_size);
+ break;
+ case JEDEC_BE_D8:
+ if (!emu_jedec_be_d8_size)
+ break;
+ if (writecnt != JEDEC_BE_D8_OUTSIZE) {
+ msg_perr("BLOCK ERASE 0xd8 outsize invalid!\n");
+ return 1;
+ }
+ if (readcnt != JEDEC_BE_D8_INSIZE) {
+ msg_perr("BLOCK ERASE 0xd8 insize invalid!\n");
+ return 1;
+ }
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ if (offs & (emu_jedec_be_d8_size - 1))
+ msg_pdbg("Unaligned BLOCK ERASE 0xd8: 0x%x\n", offs);
+ offs &= ~(emu_jedec_be_d8_size - 1);
+ memset(flashchip_contents + offs, 0xff, emu_jedec_be_d8_size);
+ break;
+ case JEDEC_CE_60:
+ if (!emu_jedec_ce_60_size)
+ break;
+ if (writecnt != JEDEC_CE_60_OUTSIZE) {
+ msg_perr("CHIP ERASE 0x60 outsize invalid!\n");
+ return 1;
+ }
+ if (readcnt != JEDEC_CE_60_INSIZE) {
+ msg_perr("CHIP ERASE 0x60 insize invalid!\n");
+ return 1;
+ }
+ /* JEDEC_CE_60_OUTSIZE is 1 (no address) -> no offset. */
+ /* emu_jedec_ce_60_size is emu_chip_size. */
+ memset(flashchip_contents, 0xff, emu_jedec_ce_60_size);
+ break;
+ case JEDEC_CE_C7:
+ if (!emu_jedec_ce_c7_size)
+ break;
+ if (writecnt != JEDEC_CE_C7_OUTSIZE) {
+ msg_perr("CHIP ERASE 0xc7 outsize invalid!\n");
+ return 1;
+ }
+ if (readcnt != JEDEC_CE_C7_INSIZE) {
+ msg_perr("CHIP ERASE 0xc7 insize invalid!\n");
+ return 1;
+ }
+ /* JEDEC_CE_C7_OUTSIZE is 1 (no address) -> no offset. */
+ /* emu_jedec_ce_c7_size is emu_chip_size. */
+ memset(flashchip_contents, 0xff, emu_jedec_ce_c7_size);
+ break;
+ case JEDEC_SFDP:
+ if (emu_chip != EMULATE_MACRONIX_MX25L6436)
+ break;
+ if (writecnt < 4)
+ break;
+ offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+
+ /* SFDP expects one dummy byte after the address. */
+ if (writecnt == 4) {
+ /* The dummy byte was not written, make sure it is read instead.
+ * Shifting and shortening the read array does achieve this goal.
+ */
+ readarr++;
+ readcnt--;
+ } else {
+ /* The response is shifted if more than 5 bytes are written, because SFDP data is
+ * already shifted out by the chip while those superfluous bytes are written. */
+ offs += writecnt - 5;
+ }
+
+ /* The SFDP spec implies that the start address of an SFDP read may be truncated to fit in the
+ * SFDP table address space, i.e. the start address may be wrapped around at SFDP table size.
+ * This is a reasonable implementation choice in hardware because it saves a few gates. */
+ if (offs >= sizeof(sfdp_table)) {
+ msg_pdbg("Wrapping the start address around the SFDP table boundary (using 0x%x "
+ "instead of 0x%x).\n", (unsigned int)(offs % sizeof(sfdp_table)), offs);
+ offs %= sizeof(sfdp_table);
+ }
+ toread = min(sizeof(sfdp_table) - offs, readcnt);
+ memcpy(readarr, sfdp_table + offs, toread);
+ if (toread < readcnt)
+ msg_pdbg("Crossing the SFDP table boundary in a single "
+ "continuous chunk produces undefined results "
+ "after that point.\n");
+ break;
+ default:
+ /* No special response. */
+ break;
+ }
+ if (writearr[0] != JEDEC_WREN && writearr[0] != JEDEC_EWSR)
+ emu_status &= ~SPI_SR_WEL;
+ return 0;
+}
+#endif
+
+static int dummy_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+
+ msg_pspew("%s:", __func__);
+
+ msg_pspew(" writing %u bytes:", writecnt);
+ for (i = 0; i < writecnt; i++)
+ msg_pspew(" 0x%02x", writearr[i]);
+
+ /* Response for unknown commands and missing chip is 0xff. */
+ memset(readarr, 0xff, readcnt);
+#if EMULATE_SPI_CHIP
+ switch (emu_chip) {
+ case EMULATE_ST_M25P10_RES:
+ case EMULATE_SST_SST25VF040_REMS:
+ case EMULATE_SST_SST25VF032B:
+ case EMULATE_MACRONIX_MX25L6436:
+ if (emulate_spi_chip_response(writecnt, readcnt, writearr,
+ readarr)) {
+ msg_pdbg("Invalid command sent to flash chip!\n");
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+ msg_pspew(" reading %u bytes:", readcnt);
+ for (i = 0; i < readcnt; i++)
+ msg_pspew(" 0x%02x", readarr[i]);
+ msg_pspew("\n");
+ return 0;
+}
+
+static int dummy_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return spi_write_chunked(flash, buf, start, len,
+ spi_write_256_chunksize);
+}
diff --git a/en29lv640b.c b/en29lv640b.c
new file mode 100644
index 0000000..3a5f611
--- /dev/null
+++ b/en29lv640b.c
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2012 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+/*
+ * WARNING!
+ * This chip uses the standard JEDEC addresses in 16-bit mode as word
+ * addresses. In byte mode, 0xAAA has to be used instead of 0x555 and
+ * 0x555 instead of 0x2AA. Do *not* blindly replace with standard JEDEC
+ * functions.
+ */
+
+/* chunksize is 1 */
+int write_en29lv640b(struct flashctx *flash, const uint8_t *src, unsigned int start, unsigned int len)
+{
+ int i;
+ chipaddr bios = flash->virtual_memory;
+ chipaddr dst = flash->virtual_memory + start;
+
+ for (i = 0; i < len; i += 2) {
+ chip_writeb(flash, 0xAA, bios + 0xAAA);
+ chip_writeb(flash, 0x55, bios + 0x555);
+ chip_writeb(flash, 0xA0, bios + 0xAAA);
+
+ /* Transfer data from source to destination. */
+ chip_writew(flash, (*src) | ((*(src + 1)) << 8 ), dst);
+ toggle_ready_jedec(flash, dst);
+#if 0
+ /* We only want to print something in the error case. */
+ msg_cerr("Value in the flash at address 0x%lx = %#x, want %#x\n",
+ (dst - bios), chip_readb(flash, dst), *src);
+#endif
+ dst += 2;
+ src += 2;
+ }
+
+ /* FIXME: Ignore errors for now. */
+ return 0;
+}
+
+int probe_en29lv640b(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint16_t id1, id2;
+
+ chip_writeb(flash, 0xAA, bios + 0xAAA);
+ chip_writeb(flash, 0x55, bios + 0x555);
+ chip_writeb(flash, 0x90, bios + 0xAAA);
+
+ programmer_delay(10);
+
+ id1 = chip_readb(flash, bios + 0x200);
+ id1 |= (chip_readb(flash, bios) << 8);
+
+ id2 = chip_readb(flash, bios + 0x02);
+
+ chip_writeb(flash, 0xF0, bios + 0xAAA);
+
+ programmer_delay(10);
+
+ msg_cdbg("%s: id1 0x%04x, id2 0x%04x\n", __func__, id1, id2);
+
+ if (id1 == flash->chip->manufacture_id && id2 == flash->chip->model_id)
+ return 1;
+
+ return 0;
+}
diff --git a/flash.h b/flash.h
new file mode 100644
index 0000000..da049d1
--- /dev/null
+++ b/flash.h
@@ -0,0 +1,367 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __FLASH_H__
+#define __FLASH_H__ 1
+
+#include "platform.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#if IS_WINDOWS
+#include <windows.h>
+#undef min
+#undef max
+#endif
+
+#define ERROR_PTR ((void*)-1)
+
+/* Error codes */
+#define ERROR_OOM -100
+#define TIMEOUT_ERROR -101
+
+/* TODO: check using code for correct usage of types */
+typedef uintptr_t chipaddr;
+#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
+
+/* Types and macros regarding the maximum flash space size supported by generic code. */
+typedef uint32_t chipoff_t; /* Able to store any addressable offset within a supported flash memory. */
+typedef uint32_t chipsize_t; /* Able to store the number of bytes of any supported flash memory. */
+#define FL_MAX_CHIPOFF_BITS (24)
+#define FL_MAX_CHIPOFF ((chipoff_t)(1ULL<<FL_MAX_CHIPOFF_BITS)-1)
+#define PRIxCHIPOFF "06"PRIx32
+#define PRIuCHIPSIZE PRIu32
+
+int register_shutdown(int (*function) (void *data), void *data);
+int shutdown_free(void *data);
+void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len);
+void programmer_unmap_flash_region(void *virt_addr, size_t len);
+void programmer_delay(unsigned int usecs);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+enum chipbustype {
+ BUS_NONE = 0,
+ BUS_PARALLEL = 1 << 0,
+ BUS_LPC = 1 << 1,
+ BUS_FWH = 1 << 2,
+ BUS_SPI = 1 << 3,
+ BUS_PROG = 1 << 4,
+ BUS_NONSPI = BUS_PARALLEL | BUS_LPC | BUS_FWH,
+};
+
+/*
+ * The following enum defines possible write granularities of flash chips. These tend to reflect the properties
+ * of the actual hardware not necesserily the write function(s) defined by the respective struct flashchip.
+ * The latter might (and should) be more precisely specified, e.g. they might bail out early if their execution
+ * would result in undefined chip contents.
+ */
+enum write_granularity {
+ /* We assume 256 byte granularity by default. */
+ write_gran_256bytes = 0,/* If less than 256 bytes are written, the unwritten bytes are undefined. */
+ write_gran_1bit, /* Each bit can be cleared individually. */
+ write_gran_1byte, /* A byte can be written once. Further writes to an already written byte cause
+ * its contents to be either undefined or to stay unchanged. */
+ write_gran_128bytes, /* If less than 128 bytes are written, the unwritten bytes are undefined. */
+ write_gran_264bytes, /* If less than 264 bytes are written, the unwritten bytes are undefined. */
+ write_gran_512bytes, /* If less than 512 bytes are written, the unwritten bytes are undefined. */
+ write_gran_528bytes, /* If less than 528 bytes are written, the unwritten bytes are undefined. */
+ write_gran_1024bytes, /* If less than 1024 bytes are written, the unwritten bytes are undefined. */
+ write_gran_1056bytes, /* If less than 1056 bytes are written, the unwritten bytes are undefined. */
+ write_gran_1byte_implicit_erase, /* EEPROMs and other chips with implicit erase and 1-byte writes. */
+};
+
+/*
+ * How many different contiguous runs of erase blocks with one size each do
+ * we have for a given erase function?
+ */
+#define NUM_ERASEREGIONS 5
+
+/*
+ * How many different erase functions do we have per chip?
+ * Atmel AT25FS010 has 6 different functions.
+ */
+#define NUM_ERASEFUNCTIONS 6
+
+/* Feature bits used for non-SPI only */
+#define FEATURE_REGISTERMAP (1 << 0)
+#define FEATURE_LONG_RESET (0 << 4)
+#define FEATURE_SHORT_RESET (1 << 4)
+#define FEATURE_EITHER_RESET FEATURE_LONG_RESET
+#define FEATURE_RESET_MASK (FEATURE_LONG_RESET | FEATURE_SHORT_RESET)
+#define FEATURE_ADDR_FULL (0 << 2)
+#define FEATURE_ADDR_MASK (3 << 2)
+#define FEATURE_ADDR_2AA (1 << 2)
+#define FEATURE_ADDR_AAA (2 << 2)
+#define FEATURE_ADDR_SHIFTED (1 << 5)
+/* Feature bits used for SPI only */
+#define FEATURE_WRSR_EWSR (1 << 6)
+#define FEATURE_WRSR_WREN (1 << 7)
+#define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
+#define FEATURE_OTP (1 << 8)
+#define FEATURE_QPI (1 << 9)
+
+enum test_state {
+ OK = 0,
+ NT = 1, /* Not tested */
+ BAD, /* Known to not work */
+ DEP, /* Support depends on configuration (e.g. Intel flash descriptor) */
+ NA, /* Not applicable (e.g. write support on ROM chips) */
+};
+
+#define TEST_UNTESTED (struct tested){ .probe = NT, .read = NT, .erase = NT, .write = NT }
+
+#define TEST_OK_PROBE (struct tested){ .probe = OK, .read = NT, .erase = NT, .write = NT }
+#define TEST_OK_PR (struct tested){ .probe = OK, .read = OK, .erase = NT, .write = NT }
+#define TEST_OK_PRE (struct tested){ .probe = OK, .read = OK, .erase = OK, .write = NT }
+#define TEST_OK_PREW (struct tested){ .probe = OK, .read = OK, .erase = OK, .write = OK }
+
+#define TEST_BAD_PROBE (struct tested){ .probe = BAD, .read = NT, .erase = NT, .write = NT }
+#define TEST_BAD_PR (struct tested){ .probe = BAD, .read = BAD, .erase = NT, .write = NT }
+#define TEST_BAD_PRE (struct tested){ .probe = BAD, .read = BAD, .erase = BAD, .write = NT }
+#define TEST_BAD_PREW (struct tested){ .probe = BAD, .read = BAD, .erase = BAD, .write = BAD }
+
+struct flashctx;
+typedef int (erasefunc_t)(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
+struct flashchip {
+ const char *vendor;
+ const char *name;
+
+ enum chipbustype bustype;
+
+ /*
+ * With 32bit manufacture_id and model_id we can cover IDs up to
+ * (including) the 4th bank of JEDEC JEP106W Standard Manufacturer's
+ * Identification code.
+ */
+ uint32_t manufacture_id;
+ uint32_t model_id;
+
+ /* Total chip size in kilobytes */
+ unsigned int total_size;
+ /* Chip page size in bytes */
+ unsigned int page_size;
+ int feature_bits;
+
+ /* Indicate how well flashrom supports different operations of this flash chip. */
+ struct tested {
+ enum test_state probe;
+ enum test_state read;
+ enum test_state erase;
+ enum test_state write;
+ } tested;
+
+ int (*probe) (struct flashctx *flash);
+
+ /* Delay after "enter/exit ID mode" commands in microseconds.
+ * NB: negative values have special meanings, see TIMING_* below.
+ */
+ signed int probe_timing;
+
+ /*
+ * Erase blocks and associated erase function. Any chip erase function
+ * is stored as chip-sized virtual block together with said function.
+ * The first one that fits will be chosen. There is currently no way to
+ * influence that behaviour. For testing just comment out the other
+ * elements or set the function pointer to NULL.
+ */
+ struct block_eraser {
+ struct eraseblock {
+ unsigned int size; /* Eraseblock size in bytes */
+ unsigned int count; /* Number of contiguous blocks with that size */
+ } eraseblocks[NUM_ERASEREGIONS];
+ /* a block_erase function should try to erase one block of size
+ * 'blocklen' at address 'blockaddr' and return 0 on success. */
+ int (*block_erase) (struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen);
+ } block_erasers[NUM_ERASEFUNCTIONS];
+
+ int (*printlock) (struct flashctx *flash);
+ int (*unlock) (struct flashctx *flash);
+ int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+ int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+ struct voltage {
+ uint16_t min;
+ uint16_t max;
+ } voltage;
+ enum write_granularity gran;
+};
+
+struct flashctx {
+ struct flashchip *chip;
+ /* FIXME: The memory mappings should be saved in a more structured way. */
+ /* The physical_* fields store the respective addresses in the physical address space of the CPU. */
+ uintptr_t physical_memory;
+ /* The virtual_* fields store where the respective physical address is mapped into flashrom's address
+ * space. A value equivalent to (chipaddr)ERROR_PTR indicates an invalid mapping (or none at all). */
+ chipaddr virtual_memory;
+ /* Some flash devices have an additional register space; semantics are like above. */
+ uintptr_t physical_registers;
+ chipaddr virtual_registers;
+ struct registered_master *mst;
+};
+
+/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
+ * field and zero delay.
+ *
+ * SPI devices will always have zero delay and ignore this field.
+ */
+#define TIMING_FIXME -1
+/* this is intentionally same value as fixme */
+#define TIMING_IGNORED -1
+#define TIMING_ZERO -2
+
+extern const struct flashchip flashchips[];
+extern const unsigned int flashchips_size;
+
+void chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+void chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr);
+void chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr);
+void chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len);
+uint8_t chip_readb(const struct flashctx *flash, const chipaddr addr);
+uint16_t chip_readw(const struct flashctx *flash, const chipaddr addr);
+uint32_t chip_readl(const struct flashctx *flash, const chipaddr addr);
+void chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len);
+
+/* print.c */
+int print_supported(void);
+void print_supported_wiki(void);
+
+/* helpers.c */
+uint32_t address_to_bits(uint32_t addr);
+int bitcount(unsigned long a);
+int max(int a, int b);
+int min(int a, int b);
+char *strcat_realloc(char *dest, const char *src);
+void tolower_string(char *str);
+#ifdef __MINGW32__
+char* strtok_r(char *str, const char *delim, char **nextp);
+#endif
+#if defined(__DJGPP__) || !defined(HAVE_STRNLEN)
+size_t strnlen(const char *str, size_t n);
+#endif
+
+/* flashrom.c */
+extern const char flashrom_version[];
+extern const char *chip_to_probe;
+int map_flash(struct flashctx *flash);
+void unmap_flash(struct flashctx *flash);
+int read_memmapped(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int erase_flash(struct flashctx *flash);
+int probe_flash(struct registered_master *mst, int startchip, struct flashctx *fill_flash, int force);
+int read_flash_to_file(struct flashctx *flash, const char *filename);
+char *extract_param(const char *const *haystack, const char *needle, const char *delim);
+int verify_range(struct flashctx *flash, const uint8_t *cmpbuf, unsigned int start, unsigned int len);
+int need_erase(const uint8_t *have, const uint8_t *want, unsigned int len, enum write_granularity gran);
+void print_version(void);
+void print_buildinfo(void);
+void print_banner(void);
+void list_programmers_linebreak(int startcol, int cols, int paren);
+int selfcheck(void);
+int doit(struct flashctx *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it);
+int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
+int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename);
+
+/* Something happened that shouldn't happen, but we can go on. */
+#define ERROR_NONFATAL 0x100
+
+/* Something happened that shouldn't happen, we'll abort. */
+#define ERROR_FATAL -0xee
+#define ERROR_FLASHROM_BUG -200
+/* We reached one of the hardcoded limits of flashrom. This can be fixed by
+ * increasing the limit of a compile-time allocation or by switching to dynamic
+ * allocation.
+ * Note: If this warning is triggered, check first for runaway registrations.
+ */
+#define ERROR_FLASHROM_LIMIT -201
+
+/* cli_common.c */
+char *flashbuses_to_text(enum chipbustype bustype);
+void print_chip_support_status(const struct flashchip *chip);
+
+/* cli_output.c */
+extern int verbose_screen;
+extern int verbose_logfile;
+#ifndef STANDALONE
+int open_logfile(const char * const filename);
+int close_logfile(void);
+void start_logging(void);
+#endif
+enum msglevel {
+ MSG_ERROR = 0,
+ MSG_WARN = 1,
+ MSG_INFO = 2,
+ MSG_DEBUG = 3,
+ MSG_DEBUG2 = 4,
+ MSG_SPEW = 5,
+};
+/* Let gcc and clang check for correct printf-style format strings. */
+int print(enum msglevel level, const char *fmt, ...)
+#ifdef __MINGW32__
+__attribute__((format(gnu_printf, 2, 3)));
+#else
+__attribute__((format(printf, 2, 3)));
+#endif
+#define msg_gerr(...) print(MSG_ERROR, __VA_ARGS__) /* general errors */
+#define msg_perr(...) print(MSG_ERROR, __VA_ARGS__) /* programmer errors */
+#define msg_cerr(...) print(MSG_ERROR, __VA_ARGS__) /* chip errors */
+#define msg_gwarn(...) print(MSG_WARN, __VA_ARGS__) /* general warnings */
+#define msg_pwarn(...) print(MSG_WARN, __VA_ARGS__) /* programmer warnings */
+#define msg_cwarn(...) print(MSG_WARN, __VA_ARGS__) /* chip warnings */
+#define msg_ginfo(...) print(MSG_INFO, __VA_ARGS__) /* general info */
+#define msg_pinfo(...) print(MSG_INFO, __VA_ARGS__) /* programmer info */
+#define msg_cinfo(...) print(MSG_INFO, __VA_ARGS__) /* chip info */
+#define msg_gdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* general debug */
+#define msg_pdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* programmer debug */
+#define msg_cdbg(...) print(MSG_DEBUG, __VA_ARGS__) /* chip debug */
+#define msg_gdbg2(...) print(MSG_DEBUG2, __VA_ARGS__) /* general debug2 */
+#define msg_pdbg2(...) print(MSG_DEBUG2, __VA_ARGS__) /* programmer debug2 */
+#define msg_cdbg2(...) print(MSG_DEBUG2, __VA_ARGS__) /* chip debug2 */
+#define msg_gspew(...) print(MSG_SPEW, __VA_ARGS__) /* general debug spew */
+#define msg_pspew(...) print(MSG_SPEW, __VA_ARGS__) /* programmer debug spew */
+#define msg_cspew(...) print(MSG_SPEW, __VA_ARGS__) /* chip debug spew */
+
+/* layout.c */
+int register_include_arg(char *name);
+int process_include_args(void);
+int read_romlayout(const char *name);
+int normalize_romentries(const struct flashctx *flash);
+int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents);
+void layout_cleanup(void);
+
+/* spi.c */
+struct spi_command {
+ unsigned int writecnt;
+ unsigned int readcnt;
+ const unsigned char *writearr;
+ unsigned char *readarr;
+};
+int spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
+int spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds);
+uint32_t spi_get_valid_read_addr(struct flashctx *flash);
+
+enum chipbustype get_buses_supported(void);
+#endif /* !__FLASH_H__ */
diff --git a/flashchips.c b/flashchips.c
new file mode 100644
index 0000000..40b6b8e
--- /dev/null
+++ b/flashchips.c
@@ -0,0 +1,16092 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp
+ * Copyright (C) 2005-2008 coresystems GmbH <stepan@openbios.org>
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+
+/**
+ * List of supported flash chips.
+ *
+ * Please keep the list sorted by vendor name and chip family, so that the output of 'flashrom -L' is roughly
+ * alphabetically sorted. Within families keep them in order of density.
+ */
+const struct flashchip flashchips[] = {
+
+ /*
+ * .vendor = Vendor name
+ * .name = Chip name
+ * .bustype = Supported flash bus types (Parallel, LPC...)
+ * .manufacture_id = Manufacturer chip ID
+ * .model_id = Model chip ID
+ * .total_size = Total size in (binary) kbytes
+ * .page_size = Page or eraseblock(?) size in bytes
+ * .tested = Test status
+ * .probe = Probe function
+ * .probe_timing = Probe function delay
+ * .block_erasers[] = Array of erase layouts and erase functions
+ * {
+ * .eraseblocks[] = Array of { blocksize, blockcount }
+ * .block_erase = Block erase function
+ * }
+ * .printlock = Chip lock status function
+ * .unlock = Chip unlock function
+ * .write = Chip write function
+ * .read = Chip read function
+ * .voltage = Voltage range in millivolt
+ */
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F010,
+ .total_size = 128,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {16 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F010A/B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F010,
+ .total_size = 128,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {16 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F002(N)BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F002BB,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_SHORT_RESET | FEATURE_ADDR_2AA,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F002(N)BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F002BT,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_EITHER_RESET | FEATURE_ADDR_2AA,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F016D",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F016D,
+ .total_size = 2 * 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F040B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F080",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F080,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29F080B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29F080,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV001BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV001BB,
+ .total_size = 128,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ {16 * 1024, 7},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -45R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV001BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV001BT,
+ .total_size = 128,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 7},
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -45R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV002BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV002BB,
+ .total_size = 256,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -55, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV002BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV002BT,
+ .total_size = 256,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -55, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV004BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV004BB,
+ .total_size = 512,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV004BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV004BT,
+ .total_size = 512,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV008BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV008BB,
+ .total_size = 1024,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 15},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600} /* 3.0-3.6V for type -70R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV008BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV008BT,
+ .total_size = 1024,
+ .page_size = 64 * 1024, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 15},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600} /* 3.0-3.6V for type -70R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV040B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -60R, others 2.7-3.6V*/
+ },
+
+ {
+ .vendor = "AMD",
+ .name = "Am29LV081B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID,
+ .model_id = AMD_AM29LV080B,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET, /* datasheet specifies address as don't care */
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -70R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L05PT",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L05PT,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L05PU",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L05PU,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L10PT",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L10PT,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 1},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L10PU",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L10PU,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 1},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L20PT",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L20PT,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L20PU",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L20PU,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ /* The A25L40P{T,U} chips are distinguished by their
+ * erase block layouts, but without any distinction in RDID.
+ * This inexplicable quirk was verified by Rudolf Marek
+ * and discussed on the flashrom mailing list on 2010-07-12.
+ */
+ {
+ .vendor = "AMIC",
+ .name = "A25L40PT",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L40PT,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L40PU",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L40PU,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L80P",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L80P,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PRE,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 15},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L16PT",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L16PT,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 31},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L16PU",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = AMIC_A25L16PU,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 31},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L512",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 16 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 1 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 64 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L010",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 32 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 2 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 128 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L020",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L020,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 64 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 4 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 256 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L040",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 128 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 8 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 512 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L080",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L080,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 256 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 16 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 1024 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L016",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L016,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 512 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 32 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 2048 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25L032",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25L032,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B, 0x48; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 1024 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 64 } },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 64 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1 } },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_amic_a25l032, /* bit5: T/B, bit6: prot size */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25LQ16",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25LQ16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, 0x48; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { { 4 * 1024, 512 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 32 } },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 32 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 2048 * 1024, 1 } },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 2048 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_amic_a25l032, /* bit5: T/B, bit6: prot size */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25LQ032/A25LQ32A",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25LQ032,
+ .total_size = 4096,
+ .page_size = 256,
+ /* A25LQ32A supports SFDP */
+ /* OTP: 64B total; read 0x4B, 0x48; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 1024 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 64 * 1024, 64 } },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 64 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1 } },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_amic_a25l032, /* bit5: T/B, bit6: prot size */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A25LQ64",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A25LQ64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ /* QPI enable 0x35, disable 0xF5 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 2048 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 256 } },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 128 } },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1 } },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1 } },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enhance (sic!) */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A29002B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A29002B,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A29002T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A29002T,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A29040B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A29040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "A49LF040A",
+ .bustype = BUS_LPC,
+ .manufacture_id = AMIC_ID_NOPREFIX,
+ .model_id = AMIC_A49LF040A,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* routine is wrapper to probe_jedec (pm49fl00x.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF021",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF021,
+ .total_size = 256,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600}, /* 2.3-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF041A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF041A,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600}, /* 2.3-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF081",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF081,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1600, 2000}, /* Datasheet says range is 1.65-1.95 V */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF081A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF081A,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF161",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF161,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PROBE,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF321",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF321,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF321A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF321A,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DF641(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF641,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DL081",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DF081,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256, /* Dual I/O (0xA2) supported */
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O (0x3B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DL161",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DL161,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256, /* Dual I/O (0xA2) supported */
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O (0x3B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25DQ161",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25DQ161,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df_sec,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect_sec,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25F512",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at25f,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f,
+ .unlock = spi_disable_blockprotect_at25f,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25F512A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F512A,
+ .total_size = 64,
+ .page_size = 128,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at25f,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f512a,
+ /* FIXME: It is not correct to use this one, because the BP1 bit is N/A. */
+ .unlock = spi_disable_blockprotect_at25f512a,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25F512B",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F512B,
+ .total_size = 64,
+ .page_size = 256,
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f512b,
+ .unlock = spi_disable_blockprotect_at25f512b,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ /* The A suffix indicates 33MHz instead of 20MHz clock rate.
+ * All other properties seem to be the same.*/
+ .name = "AT25F1024(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F1024,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at25f,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f,
+ .unlock = spi_disable_blockprotect_at25f,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25F2048",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F2048,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at25f,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f,
+ .unlock = spi_disable_blockprotect_at25f,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25F4096",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25F4096,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at25f,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_62,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25f4096,
+ /* "Bits 5-6 are 0s when device is not in an internal write cycle." Better leave them alone: */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25FS010",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25FS010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25fs010,
+ .unlock = spi_disable_blockprotect_at25fs010,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT25FS040",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT25FS040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25fs040,
+ .unlock = spi_disable_blockprotect_at25fs040,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT26DF041",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26DF041,
+ .total_size = 512,
+ .page_size = 256,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 2048} },
+ .block_erase = spi_block_erase_81,
+ }, {
+ .eraseblocks = { {2 * 1024, 256} },
+ .block_erase = spi_block_erase_50,
+ }, {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain,
+ /* Supports also an incompatible page write (of exactly 256 B) and an auto-erasing write. */
+ .write = spi_chip_write_1,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 3.0-3.6V for higher speed, 2.7-3.6V normal */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT26DF081A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26DF081A,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at26df081a,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT26DF161",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26DF161,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at25df,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT26DF161A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26DF161A,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at26df081a,
+ .unlock = spi_disable_blockprotect_at2x_global_unprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ /*The AT26DF321 has the same ID as the AT25DF321. */
+ /*{
+ .vendor = "Atmel",
+ .name = "AT26DF321",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26DF321,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .printlock = spi_prettyprint_status_register_at26df081a,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ },*/
+
+ {
+ .vendor = "Atmel",
+ .name = "AT26F004",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT26F004,
+ .total_size = 512,
+ .page_size = 256,
+ .tested = {.probe = NT, .read = NT, .erase = NT, .write = BAD },
+ .feature_bits = FEATURE_WRSR_WREN,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .write = NULL /* Incompatible Page write */,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT29C512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT29C512,
+ .total_size = 64,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10000, /* 10mS, Enter=Exec */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT29C010A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT29C010A,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = 10000, /* 10mS, Enter=Exec */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec, /* FIXME */
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT29C020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT29C020,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10000, /* 10ms */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT29C040A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT29C040A,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10000, /* 10 ms */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45CS1282",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45CS1282,
+ .total_size = 16896 /* No power of two sizes */,
+ .page_size = 1056 /* No power of two sizes */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77 (4 dummy bytes); write 0x9A (via buffer) */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1056, 1}, /* sector 0a: opcode 50h */
+ {248 * 1056, 1}, /* sector 0b: opcode 7Ch */
+ {256 * 1056, 63}, /* sectors 1 - 63: opcode 7Ch */
+ },
+ .block_erase = spi_erase_at45cs_sector,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain,
+ .gran = write_gran_1056bytes,
+ .write = spi_write_at45db,
+ .read = spi_read_at45db,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB011D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB011D,
+ .total_size = 128 /* or 132, determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 512} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 256, 512/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 256, 1},
+ {120 * 256, 1},
+ {128 * 256, 3},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB021D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB021D,
+ .total_size = 256 /* or 264, determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 1024} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 256, 1024/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 256, 1},
+ {120 * 256, 1},
+ {128 * 256, 7},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB041D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB041D,
+ .total_size = 512 /* or 528, determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 2048} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 256, 2048/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 256, 1},
+ {248 * 256, 1},
+ {256 * 256, 7},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB081D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB081D,
+ .total_size = 1024 /* or 1056, determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 4096} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 256, 4096/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 256, 1},
+ {248 * 256, 1},
+ {256 * 256, 15},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB161D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB161D,
+ .total_size = 2048 /* or 2112, determined from status register */,
+ .page_size = 512 /* or 528, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 4096} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 512, 4096/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 512, 1},
+ {248 * 512, 1},
+ {256 * 512, 15},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB321C",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB321C,
+ .total_size = 4224 /* No power of two sizes */,
+ .page_size = 528 /* No power of two sizes */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77 (4 dummy bytes); write 0x9A (via buffer) */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {528, 8192} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 528, 8192/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, /* Although the datasheets describes sectors (which can be write protected)
+ * there seems to be no erase functions for them.
+ {
+ .eraseblocks = {
+ {8 * 528, 1},
+ {120 * 528, 1},
+ {128 * 528, 63},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, */ {
+ .eraseblocks = { {4224 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_at45db, /* Bit 0 is undefined, no lockdown */
+ .gran = write_gran_528bytes,
+ .write = spi_write_at45db,
+ .read = spi_read_at45db_e8, /* 3 address and 4 dummy bytes */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB321D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB321D,
+ .total_size = 4096 /* or 4224, determined from status register */,
+ .page_size = 512 /* or 528, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 8192} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 512, 8192/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 512, 1},
+ {120 * 512, 1},
+ {128 * 512, 63},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB321E",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB321C,
+ .total_size = 4096 /* or 4224, determined from status register */,
+ .page_size = 512 /* or 528, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 8192} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 512, 8192/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 512, 1},
+ {120 * 512, 1},
+ {128 * 512, 63},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db, /* has a 2nd status register */
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2500, 3600}, /* 2.3-3.6V & 2.5-3.6V models available */
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT45DB642D",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT45DB642D,
+ .total_size = 8192 /* or 8448, determined from status register */,
+ .page_size = 1024 /* or 1056, determined from status register */,
+ /* does not support EWSR nor WREN and has no writable status register bits whatsoever */
+ /* OTP: 128B total, 64B pre-programmed; read 0x77; write 0x9B */
+ .feature_bits = FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_at45db,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 8192} },
+ .block_erase = spi_erase_at45db_page,
+ }, {
+ .eraseblocks = { {8 * 1024, 8192/8} },
+ .block_erase = spi_erase_at45db_block,
+ }, {
+ .eraseblocks = {
+ {8 * 1024, 1},
+ {248 * 1024, 1},
+ {256 * 1024, 31},
+ },
+ .block_erase = spi_erase_at45db_sector
+ }, {
+ .eraseblocks = { {8192 * 1024, 1} },
+ .block_erase = spi_erase_at45db_chip,
+ }
+ },
+ .unlock = spi_disable_blockprotect_at45db, /* Impossible if locked down or #WP is low */
+ .printlock = spi_prettyprint_status_register_at45db,
+ /* granularity will be set by the probing function. */
+ .write = spi_write_at45db,
+ .read = spi_read_at45db, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49BV512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49BV512,
+ .total_size = 64,
+ .page_size = 64,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49F002(N)",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F002N,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {96 * 1024, 1},
+ {128 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49F002(N)T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F002NT,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 1},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49(H)F010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F010,
+ .total_size = 128,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_at49f,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49F020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F020,
+ .total_size = 256,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ /* Chip features an optional permanent write protection
+ * of the first 8 kB. The erase function is the same as
+ * above, but 00000H to 01FFFH will not be erased.
+ * FIXME: add another eraser when partial erasers are
+ * supported.
+ */
+ },
+ .printlock = printlock_at49f,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49F040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F040,
+ .total_size = 512,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ /* Chip features an optional permanent write protection
+ * of the first 16 kB. The erase function is the same as
+ * above, but 00000H to 03FFFH will not be erased.
+ * FIXME: add another eraser when partial erasers are
+ * supported.
+ */
+ },
+ .printlock = printlock_at49f,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49F080",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F080,
+ .total_size = 1024,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ /* Chip features an optional permanent write protection
+ * of the first 16 kB. The erase function is the same as
+ * above, but 00000H to 03FFFH will not be erased.
+ * FIXME: add another eraser when partial erasers are
+ * supported.
+ */
+ },
+ .printlock = printlock_at49f,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ /* 'top' version of AT49F080. equal in all aspects but the boot block address */
+ .vendor = "Atmel",
+ .name = "AT49F080T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49F080T,
+ .total_size = 1024,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ /* Chip features an optional permanent write protection
+ * of the first 16 kB. The erase function is the same as
+ * above, but FC000H to FFFFFH will not be erased.
+ * FIXME: add another eraser when partial erasers are
+ * supported.
+ */
+ },
+ .printlock = printlock_at49f,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49LH002",
+ .bustype = BUS_LPC | BUS_FWH, /* A/A Mux */
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49LH002,
+ .total_size = 256,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = NULL, /* TODO: Implement. */
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 4},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49LH00B4",
+ .bustype = BUS_LPC | BUS_FWH, /* A/A Mux */
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49LH00B4,
+ .total_size = 512,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = NULL, /* TODO: Implement. */
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 8},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "AT49LH004",
+ .bustype = BUS_LPC | BUS_FWH, /* A/A Mux */
+ .manufacture_id = ATMEL_ID,
+ .model_id = ATMEL_AT49LH004,
+ .total_size = 512,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 8},
+ },
+ .block_erase = NULL, /* TODO: Implement. */
+ },
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Catalyst",
+ .name = "CAT28F512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = CATALYST_ID,
+ .model_id = CATALYST_CAT28F512,
+ .total_size = 64,
+ .page_size = 0, /* unused */
+ .feature_bits = 0,
+ .tested = {.probe = OK, .read = OK, .erase = BAD, .write = BAD },
+ .probe = probe_jedec, /* FIXME! */
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = NULL, /* TODO */
+ },
+ },
+ .write = NULL, /* TODO */
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Bright",
+ .name = "BM29F040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = BRIGHT_ID,
+ .model_id = BRIGHT_BM29F040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "ESMT",
+ .name = "F49B002UA",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ESMT_ID,
+ .model_id = ESMT_F49B002UA,
+ .total_size = 256,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 1},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "ESMT",
+ .name = "F25L008A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ESMT_ID,
+ .model_id = ESMT_F25L008A,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ESMT",
+ .name = "F25L32PA",
+ .bustype = BUS_SPI,
+ .manufacture_id = ESMT_ID,
+ .model_id = ESMT_F25L32PA,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_bpl,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B05",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B05,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B05T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B05,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P05",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B05,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B10",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 3},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B10T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {32 * 1024, 3},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P10",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B20",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 3}
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B20T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P20",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 7}
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B40T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B80",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 15}
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B80T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 15},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P80",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 31},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B16T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 31},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B32",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 63},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B32T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 63},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P32", /* Uniform version of EN25B32 */
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ {16 * 1024, 1},
+ {32 * 1024, 1},
+ {64 * 1024, 127},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25B64T",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 127},
+ {32 * 1024, 1},
+ {16 * 1024, 1},
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25P64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25B64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F05",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F05,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F10",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F20",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F80",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F32",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F32,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25F64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25F64,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25Q40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q40,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25Q80(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ /* Note: EN25D16 is an evil twin which shares the model ID
+ but has different write protection capabilities */
+ .vendor = "Eon",
+ .name = "EN25Q16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: D16 512B/Q16 128B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ /* not supported by Q16 version */
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25Q32(A/B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25Q64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25Q128",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25Q128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25QH16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25QH16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0x3A */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 2048, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 2048, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25QH32",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25QH32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0x3A */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 4096, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 4096, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25QH64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25QH64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0x3A */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25QH128",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25QH128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0x3A */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S10",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S10,
+ .total_size = 128,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S20",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S20,
+ .total_size = 256,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S40,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S80",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_en25s_wp,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S32",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_en25s_wp,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN25S64",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = EON_EN25S64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8192 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8192 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_en25s_wp,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29F010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29F010,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {16 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ },
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29F002(A)(N)B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29F002B,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29F002(A)(N)T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29F002T,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29LV040(A)",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29LV040,
+ .total_size = 512,
+ .page_size = 4 * 1024,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ },
+ {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -45R and 55R, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29LV640B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29LV640B,
+ .total_size = 8192,
+ .page_size = 8192,
+ .feature_bits = FEATURE_ADDR_SHIFTED,
+ .tested = TEST_OK_PREW,
+ .probe = probe_en29lv640b,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127},
+ },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_en29lv640b,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29GL064(A)B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29GL064B,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29GL064(A)T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29GL064T,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 127},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29GL064H/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29GL064HL,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "EN29GL128",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = EON_ID,
+ .model_id = EON_EN29GL128HL,
+ .total_size = 16384,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ESI",
+ .name = "ES25P40",
+ .bustype = BUS_SPI,
+ .manufacture_id = EXCEL_ID_NOPREFIX,
+ .model_id = EXCEL_ES25P40,
+ .total_size = 512,
+ .page_size = 256,
+ /* 256-byte paramter page separate from memory array:
+ * supports read (0x53), fast read (0x5B), erase (0xD5) and program (0x52) instructions. */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast Read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ESI",
+ .name = "ES25P80",
+ .bustype = BUS_SPI,
+ .manufacture_id = EXCEL_ID_NOPREFIX,
+ .model_id = EXCEL_ES25P80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* 256-byte paramter page separate from memory array:
+ * supports read (0x53), fast read (0x5B), erase (0xD5) and program (0x52) instructions. */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast Read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ESI",
+ .name = "ES25P16",
+ .bustype = BUS_SPI,
+ .manufacture_id = EXCEL_ID_NOPREFIX,
+ .model_id = EXCEL_ES25P16,
+ .total_size = 2 * 1024,
+ .page_size = 256,
+ /* 256-byte paramter page separate from memory array:
+ * supports read (0x53), fast read (0x5B), erase (0xD5) and program (0x52) instructions. */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast Read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Fujitsu",
+ .name = "MBM29F004BC",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29F004BC,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = NULL,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Fujitsu",
+ .name = "MBM29F004TC",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29F004TC,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = NULL,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ /* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+ .vendor = "Fujitsu",
+ .name = "MBM29F400BC",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29F400BC,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "Fujitsu",
+ .name = "MBM29F400TC",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29F400TC,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -55, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "Fujitsu",
+ .name = "MBM29LV160BE",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29LV160BE,
+ .total_size = 2 * 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 31},
+ },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1, /* Supports a fast mode too */
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -70, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "Fujitsu",
+ .name = "MBM29LV160TE",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = FUJITSU_ID,
+ .model_id = FUJITSU_MBM29LV160TE,
+ .total_size = 2 * 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 31},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1, /* Supports a fast mode too */
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* 3.0-3.6V for type -70, others 2.7-3.6V */
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ40",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ40,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ80",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ16",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ32",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ64(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25LQ128",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25LQ128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1695, 1950},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q512",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q10",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q20(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q40(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q80(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (B version only) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q16(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (B version only) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q32(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q64(B)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q128B",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25Q128C",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25Q128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 1536B total; read 0x48; write 0x42, erase 0x44 */
+ /* QPI: enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: 2nd status reg (read 0x35, write 0x31) and 3rd status reg (read 0x15, write 0x11) */
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25T80",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25T80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0x3A */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25VQ21B",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25VQ21B,
+ .total_size = 256,
+ .page_size = 256,
+ /* OTP: 1536B total; read 0x48, write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25VQ40C",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25VQ41B,
+ .total_size = 512,
+ .page_size = 256,
+ /* Supports SFDP */
+ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25VQ41B",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25VQ41B,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 1536B total; read 0x48, write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25VQ80C",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25VQ80C,
+ .total_size = 1024,
+ .page_size = 256,
+ /* Supports SFDP */
+ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "GigaDevice",
+ .name = "GD25VQ16C",
+ .bustype = BUS_SPI,
+ .manufacture_id = GIGADEVICE_ID,
+ .model_id = GIGADEVICE_GD25VQ16C,
+ .total_size = 2 * 1024,
+ .page_size = 256,
+ /* Supports SFDP */
+ /* OTP: 1024B total; read 0x48, write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { { 32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { { 64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp4_srwd,
+ .unlock = spi_disable_blockprotect_bp4_srwd, /* TODO: 2nd status reg (read with 0x35) */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "Hyundai",
+ .name = "HY29F002T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = HYUNDAI_ID,
+ .model_id = HYUNDAI_HY29F002T,
+ .total_size = 256,
+ .page_size = 256 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -45, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "Hyundai",
+ .name = "HY29F002B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = HYUNDAI_ID,
+ .model_id = HYUNDAI_HY29F002B,
+ .total_size = 256,
+ .page_size = 256 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET, /* Some revisions may need FEATURE_ADDR_2AA */
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -45, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "Hyundai",
+ .name = "HY29F040A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = HYUNDAI_ID,
+ .model_id = HYUNDAI_HY29F040A,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F160S33B8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F160S33B8,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 31} // inaccessible
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F160S33T8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F160S33T8,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {64 * 1024, 31}, // inaccessible
+ {8 * 1024, 8}
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F320S33B8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F320S33B8,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 63} // inaccessible
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F320S33T8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F320S33T8,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {64 * 1024, 63}, // inaccessible
+ {8 * 1024, 8}
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F640S33B8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F640S33B8,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127} // inaccessible
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "25F640S33T8",
+ .bustype = BUS_SPI,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_25F640S33T8,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 506B total (2x 8B, 30x 16B, 1x 10B); read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ /* This chip supports erasing of the 8 so-called "parameter blocks" with
+ * opcode 0x40. Trying to access an address outside these 8 8kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {64 * 1024, 127}, // inaccessible
+ {8 * 1024, 8}
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd,
+ .unlock = spi_disable_blockprotect_bp2_ep_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* also fast read 0x0B */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F001BN/BX-B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F001B,
+ .total_size = 128,
+ .page_size = 128 * 1024, /* 8k + 2x4k + 112k */
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ {112 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F001BN/BX-T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F001T,
+ .total_size = 128,
+ .page_size = 128 * 1024, /* 112k + 2x4k + 8k */
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {112 * 1024, 1},
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F002BC/BL/BV/BX-T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F002T,
+ .total_size = 256,
+ .page_size = 256 * 1024,
+ .tested = TEST_OK_PRE,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 1},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F008S3/S5/SC",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F004S3,
+ .total_size = 512,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .unlock = unlock_28f004s5,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F004B5/BE/BV/BX-B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F004B,
+ .total_size = 512,
+ .page_size = 128 * 1024, /* maximal block size */
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {96 * 1024, 1},
+ {128 * 1024, 3},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F004B5/BE/BV/BX-T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F004T,
+ .total_size = 512,
+ .page_size = 128 * 1024, /* maximal block size */
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 3},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F400BV/BX/CE/CV-B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F400B,
+ .total_size = 512,
+ .page_size = 128 * 1024, /* maximal block size */
+ .feature_bits = FEATURE_ADDR_SHIFTED,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {96 * 1024, 1},
+ {128 * 1024, 3},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "28F400BV/BX/CE/CV-T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_28F400T,
+ .total_size = 512,
+ .page_size = 128 * 1024, /* maximal block size */
+ .feature_bits = FEATURE_ADDR_SHIFTED,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 3},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .write = write_82802ab,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "82802AB",
+ .bustype = BUS_FWH,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_82802AB,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Intel",
+ .name = "82802AC",
+ .bustype = BUS_FWH,
+ .manufacture_id = INTEL_ID,
+ .model_id = INTEL_82802AC,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine does not use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_82802ab,
+ },
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "ISSI",
+ .name = "IS29GL064B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ISSI_ID,
+ .model_id = ISSI_PMC_IS29GL064B,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ISSI",
+ .name = "IS29GL064T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ISSI_ID,
+ .model_id = ISSI_PMC_IS29GL064T,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 127},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ISSI",
+ .name = "IS29GL064H/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ISSI_ID,
+ .model_id = ISSI_PMC_IS29GL064HL,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ISSI",
+ .name = "IS29GL128H/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ISSI_ID,
+ .model_id = ISSI_PMC_IS29GL128HL,
+ .total_size = 16384,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX23L1654",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX23L1654,
+ .total_size = 2048,
+ .page_size = 256,
+ .tested = {.probe = NT, .read = NT, .erase = NA, .write = NA},
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL, /* MX23L1654 is a mask ROM, so it is read-only */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX23L3254",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX23L3254,
+ .total_size = 4096,
+ .page_size = 256,
+ .tested = {.probe = OK, .read = OK, .erase = NA, .write = NA},
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL, /* MX23L3254 is a mask ROM, so it is read-only */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX23L6454",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX23L6454,
+ .total_size = 8192,
+ .page_size = 256,
+ .tested = {.probe = OK, .read = OK, .erase = NA, .write = NA},
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL, /* MX23L6454 is a mask ROM, so it is read-only */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX23L12854",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX23L12854,
+ .total_size = 16384,
+ .page_size = 256,
+ .tested = {.probe = NT, .read = NT, .erase = NA, .write = NA},
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL, /* MX23L12854 is a mask ROM, so it is read-only */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L512(E)/MX25V512(C)",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L512,
+ .total_size = 64,
+ .page_size = 256,
+ /* MX25L512E supports SFDP */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported, MX25L512E supports dual I/O */
+ .voltage = {2700, 3600}, /* 2.35-3.6V for MX25V512(C) */
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1005(C)/MX25L1006E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1005,
+ .total_size = 128,
+ .page_size = 256,
+ /* MX25L1006E supports SFDP */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported, MX25L1006E supports dual I/O */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L2005(C)/MX25L2006E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L2005,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L4005(A/C)/MX25L4006E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L4005,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L8005/MX25L8006E/MX25L8008E/MX25V8005",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L8005,
+ .total_size = 1024,
+ .page_size = 256,
+ /* MX25L8006E, MX25L8008E support SFDP */
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 (MX25L8006E, MX25L8008E only) */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600}, /* 2.35-3.6V for MX25V8005 */
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1605",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1605,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit6: error flag */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1605A/MX25L1606E/MX25L1608E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1605,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 (MX25L1606E and MX25L1608E only) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* MX25L1605A bp2 only */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported (MX25L1608E supports dual-I/O read) */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1605D/MX25L1608D/MX25L1673E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1605,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6: Continuously Program (CP) mode, for 73E is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1635D",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1635D,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L1635E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L1635E,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L3205(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L3205,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit6: error flag */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L3205D/MX25L3208D",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L3205,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6: continuously program mode */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L3206E/MX25L3208E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L3205,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L3273E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L3205,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L3235D",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L3235D,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 256B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L6405",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L6405,
+ .total_size = 8192,
+ .page_size = 256,
+ /* Has an additional 512B EEPROM sector */
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6: error flag */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L6405D",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L6405,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6: continuously program mode */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0xBB) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L6406E/MX25L6408E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L6405,
+ .total_size = 8192,
+ .page_size = 256,
+ /* MX25L6406E supports SFDP */
+ /* OTP: 06E 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L6405,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L12805D",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L12805D,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 64B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L12835F/MX25L12845E/MX25L12865E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L12805D,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register and SBLK/SBULK; MX25L12835F: configuration register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25U1635E",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25U1635E,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ /* QPI enable 0x35, disable 0xF5 (0xFF et al. work too) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1650, 2000},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25U3235E/F",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25U3235E,
+ .total_size = 4096,
+ .page_size = 256,
+ /* F model supports SFDP */
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ /* QPI enable 0x35, disable 0xF5 (0xFF et al. work too) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1650, 2000},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25U6435E/F",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25U6435E,
+ .total_size = 8192,
+ .page_size = 256,
+ /* F model supports SFDP */
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ /* QPI enable 0x35, disable 0xF5 (0xFF et al. work too) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1650, 2000},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25U12835F",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25U12835E,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 512B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ /* TODO: security register */
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1650, 2000},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX25L6495F",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX25L6495F,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 1024B total; enter 0xB1, exit 0xC1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F001B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F001B,
+ .total_size = 128,
+ .page_size = 32 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 1},
+ {4 * 1024, 2},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F001T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F001T,
+ .total_size = 128,
+ .page_size = 32 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 1},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {4 * 1024, 2},
+ {8 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F002(N)B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F002B,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F002(N)T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F002T,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F022(N)B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F022B,
+ .total_size = 256,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F022(N)T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F022T,
+ .total_size = 256,
+ .page_size = 0, /* unused */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29F040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29F040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL320EB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL320EB,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 63},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL320ET",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL320ET,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 63},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL320EH/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL320EHL,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL640EB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL640EB,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL640ET",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL640ET,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 127},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL640EH/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL640EHL,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29GL128F",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29GL128F,
+ .total_size = 16384,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "MX29LV040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = MACRONIX_MX29LV040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P05-A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P05A,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ /* The ST M25P05 is a bit of a problem. It has the same ID as the
+ * ST M25P05-A in RES mode, but supports only 128 byte writes instead
+ * of 256 byte writes. We rely heavily on the fact that probe_spi_res1
+ * only is successful if RDID does not work.
+ */
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P05",
+ .bustype = BUS_SPI,
+ .manufacture_id = 0, /* Not used. */
+ .model_id = ST_M25P05_RES,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res1,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_1, /* 128 */
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P10-A",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P10A,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ /* The ST M25P10 has the same problem as the M25P05. */
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P10",
+ .bustype = BUS_SPI,
+ .manufacture_id = 0, /* Not used. */
+ .model_id = ST_M25P10_RES,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res1,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_1, /* 128 */
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST", /* Numonyx */
+ .name = "M25P20",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P20-old",
+ .bustype = BUS_SPI,
+ .manufacture_id = 0, /* Not used. */
+ .model_id = ST_M25P20_RES,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res1,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST", /* Numonyx */
+ .name = "M25P40",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P40-old",
+ .bustype = BUS_SPI,
+ .manufacture_id = 0, /* Not used. */
+ .model_id = ST_M25P40_RES,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res1,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P80",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P16",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P32",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P32,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P64",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P64,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25P128",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25P128,
+ .total_size = 16384,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* TODO: check */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PE10",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PE10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PE20",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PE20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PE40",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PE40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PE80",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PE80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PE16",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PE16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PX80",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PX80,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { { 4 * 1024, 256 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit5: T/B */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PX16",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PX16,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 512 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit5: T/B */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PX32",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PX32,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PRE,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 1024 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit5: T/B */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M25PX64",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M25PX64,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { { 4 * 1024, 2048 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* bit5: T/B */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M45PE10",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M45PE10,
+ .total_size = 128,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 512} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256, /* Page write (similar to PP but allows 0->1 changes) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M45PE20",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M45PE20,
+ .total_size = 256,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 1024} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256, /* Page write (similar to PP but allows 0->1 changes) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M45PE40",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M45PE40,
+ .total_size = 512,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 2048} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256, /* Page write supported (similar to PP but allows 0->1 changes) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M45PE80",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M45PE80,
+ .total_size = 1024,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 4096} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256, /* Page write (similar to PP but allows 0->1 changes) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "M45PE16",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M45PE16,
+ .total_size = 2048,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 8192} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256, /* Page write (similar to PP but allows 0->1 changes) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q016",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q016__1E,
+ .total_size = 2048,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1700, 2000},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q032..1E",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q032__1E,
+ .total_size = 4096,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1700, 2000},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q032..3E",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q032__3E,
+ .total_size = 4096,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q064..1E", /* ..1E = 1.8V, uniform 64KB/4KB blocks/sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q064__1E,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1700, 2000},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q064..3E", /* ..3E = 3V, uniform 64KB/4KB blocks/sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q064__3E,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q128..1E", /* ..1E = 1.8V, uniform 64KB/4KB blocks/sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q128__1E,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 4096 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {1700, 2000},
+ },
+
+ {
+ .vendor = "Micron/Numonyx/ST",
+ .name = "N25Q128..3E", /* ..3E = 3V, uniform 64KB/4KB blocks/sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = ST_N25Q128__3E,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 64B total; read 0x4B, write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 4096 } },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_n25q, /* TODO: config, lock, flag regs */
+ .unlock = spi_disable_blockprotect_n25q, /* TODO: per 64kB sector lock registers */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29C51000B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29C51000B,
+ .total_size = 64,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29C51000T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29C51000T,
+ .total_size = 64,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29C51400B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29C51400B,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29C51400T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29C51400T,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29LC51000",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29LC51000,
+ .total_size = 64,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29LC51001",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29LC51001,
+ .total_size = 128,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "MoselVitelic",
+ .name = "V29LC51002",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = MVC_V29LC51002,
+ .total_size = 256,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Nantronics",
+ .name = "N25S10",
+ .bustype = BUS_SPI,
+ .manufacture_id = NANTRONICS_ID_NOPREFIX,
+ .model_id = NANTRONICS_N25S10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Nantronics",
+ .name = "N25S20",
+ .bustype = BUS_SPI,
+ .manufacture_id = NANTRONICS_ID_NOPREFIX,
+ .model_id = NANTRONICS_N25S20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Nantronics",
+ .name = "N25S40",
+ .bustype = BUS_SPI,
+ .manufacture_id = NANTRONICS_ID_NOPREFIX,
+ .model_id = NANTRONICS_N25S40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Nantronics",
+ .name = "N25S80",
+ .bustype = BUS_SPI,
+ .manufacture_id = NANTRONICS_ID_NOPREFIX,
+ .model_id = NANTRONICS_N25S80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Nantronics",
+ .name = "N25S16",
+ .bustype = BUS_SPI,
+ .manufacture_id = NANTRONICS_ID_NOPREFIX,
+ .model_id = NANTRONICS_N25S16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O read (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LD256C",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LD256C,
+ .total_size = 32,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 8} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 8} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 1} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {32 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {32 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2700, 3600},
+ },
+ {
+ .vendor = "PMC",
+ .name = "Pm25LD512(C)",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LD512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* FIXME: C version supports "Safe Guard" */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LD010(C)",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LD010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* FIXME: C version supports "Safe Guard" */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2700, 3600}, /* 2.3-3.6V for Pm25LD010 */
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LD020(C)",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LD020,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* FIXME: C version supports "Safe Guard" */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2700, 3600}, /* 2.3-3.6V for Pm25LD020 */
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LD040(C)",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual I/O supported */
+ .voltage = {2700, 3600}, /* 2.3-3.6V for Pm25LD040 */
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LQ020",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LQ020,
+ .total_size = 256,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x4B, write 0xB1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LQ040",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LQ040,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x4B, write 0xB1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LQ080",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LQ080,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B, write 0xB1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LQ016",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LQ016,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x4B, write 0xB1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LQ032C",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LQ032C,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 64B total; read 0x4B, write 0xB1 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4096 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd, /* bit6 is quad enable */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV512(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM25LV512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2, /* The continuation code is transferred as the 3rd byte m( */
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV010",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM25LV010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2, /* The continuation code is transferred as the 3rd byte m( */
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV010A",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV020",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV020,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV040",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV080B",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV080B,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm25LV016B",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = PMC_PM25LV016B,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm29F002T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM29F002T,
+ .total_size = 256,
+ .page_size = 8 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 1},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm29F002B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM29F002B,
+ .total_size = 256,
+ .page_size = 8 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {96 * 1024, 1},
+ {128 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm39LV010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM39F010, /* Pm39LV010 and Pm39F010 have identical IDs but different voltage */
+ .total_size = 128,
+ .page_size = 4096,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm39LV020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM39LV020,
+ .total_size = 256,
+ .page_size = 4096,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm39LV040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM39LV040,
+ .total_size = 512,
+ .page_size = 4096,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm39LV512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM39LV512,
+ .total_size = 64,
+ .page_size = 4096,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm49FL002",
+ .bustype = BUS_LPC | BUS_FWH, /* A/A Mux */
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM49FL002,
+ .total_size = 256,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* routine is wrapper to probe_jedec (pm49fl00x.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_32k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "Pm49FL004",
+ .bustype = BUS_LPC | BUS_FWH, /* A/A Mux */
+ .manufacture_id = PMC_ID_NOPREFIX,
+ .model_id = PMC_PM49FL004,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW106",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW106,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {2 * 1024, 64} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp1_srwd, /* FIXME: Add ERSER error flag. */
+ .unlock = spi_disable_blockprotect_bp1_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW406A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW406A,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FU406B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FU406B,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FU406C/LE25U40CMC",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FU406C,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual read (0x3B) and dual I/O (0xBB) supported */
+ .voltage = {2300, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW203A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW203A,
+ .total_size = 256,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256, 1024} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW403A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW403A,
+ .total_size = 512,
+ .page_size = 256,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256, 2 * 1024} },
+ .block_erase = spi_block_erase_db,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_default_welwip,
+ .unlock = NULL, /* #WP pin write-protects lower 64kB. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW418A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW418A,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* some quad-read supported ("HD_READ mode") */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW806",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW806,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "LE25FW808",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = SANYO_LE25FW808,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {8 * 1024, 128} },
+ .block_erase = spi_block_erase_d7,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* some quad-read supported ("HD_READ mode") */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sharp",
+ .name = "LH28F008BJT-BTLZ1",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SHARP_ID,
+ .model_id = SHARP_LH28F008BJ__PB,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .tested = TEST_OK_PREW,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 15}
+ },
+ .block_erase = erase_block_82802ab,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_sector_49lfxxxc,
+ }
+ },
+ .unlock = unlock_lh28f008bjt,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Sharp",
+ .name = "LHF00L04",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = SHARP_ID,
+ .model_id = SHARP_LHF00L04,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET | FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 15},
+ {8 * 1024, 8}
+ },
+ .block_erase = erase_block_82802ab,
+ }, {
+ .eraseblocks = {
+ {1024 * 1024, 1}
+ },
+ .block_erase = NULL, /* 30 D0, only in A/A mux mode */
+ },
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL004A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL004A,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL008A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL008A,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PRE,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL016A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL016A,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL032A/P",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL032A,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL064A/P",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL064A,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL204K",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL204,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL208K",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL208,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL116K/S25FL216K", /* FIXME: separate them */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL216,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 (S25FL116K only) */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 2048 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 2048 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and dual I/O (0x3B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL132K",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL132K,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 768B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 4096 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* TODO: improve */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL164K",
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL164K,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 8192 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd, /* TODO: improve */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL127S-64kB", /* hybrid: 32 (top or bottom) 4 kB sub-sectors + 64 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports 4B addressing */
+ /* OTP: 1024B total, 32B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ /* FIXME: we should distinguish the configuration on probing time like we do for AT45DB chips */
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ /* This chip supports erasing of 32 so-called "parameter sectors" with
+ * opcode 0x20 which may be configured to be on top or bottom of the address
+ * space. Trying to access an address outside these 4kB blocks does have no
+ * effect on the memory contents, e.g.
+ .eraseblocks = {
+ {4 * 1024, 32},
+ {64 * 1024, 254} // inaccessible
+ },
+ .block_erase = spi_block_erase_20,
+ }, { */
+ .eraseblocks = { { 64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL127S-256kB", /* uniform 256kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 512,
+ /* supports 4B addressing */
+ /* OTP: 1024B total, 32B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* #WP pin write-protects SRWP bit. */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL128P......0", /* uniform 64 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp3_srwd,
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL128P......1", /* uniform 256kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_srwd,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL128S......0", /* hybrid: 32 (top or bottom) 4 kB sub-sectors + 64 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports 4B addressing */
+ /* OTP: 1024B total, 32B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ /* This chip supports erasing of the 32 so-called "parameter sectors" with
+ * opcode 0x20. Trying to access an address outside these 4kB blocks does
+ * have no effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {4 * 1024, 32},
+ {64 * 1024, 254} // inaccessible
+ },
+ .block_erase = spi_block_erase_20,
+ }, { */
+ .eraseblocks = { { 64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd, /* TODO: SR2 and many others */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: various other locks */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL128S......1", /* uniform 256 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 512,
+ /* supports 4B addressing */
+ /* OTP: 1024B total, 32B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd, /* TODO: SR2 and many others */
+ .unlock = spi_disable_blockprotect_bp2_srwd, /* TODO: various other locks */
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL129P......0", /* hybrid: 32 (top or bottom) 4 kB sub-sectors + 64 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 506B total, 16B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ /* FIXME: This chip supports erasing of the 32 so-called "parameter sectors" with
+ * opcode 0x20. Trying to access an address outside these 4kB blocks does have no
+ * effect on the memory contents, but sets a flag in the SR.
+ .eraseblocks = {
+ {4 * 1024, 32},
+ {64 * 1024, 254} // inaccessible
+ },
+ .block_erase = spi_block_erase_20,
+ }, { */
+ /* FIXME: Additionally it also supports erase opcode 40h for the respective 2*4 kB pairs
+ .eraseblocks = {
+ {8 * 1024, 16},
+ {64 * 1024, 254} // inaccessible
+ },
+ .block_erase = spi_block_erase_40,
+ }, { */
+ .eraseblocks = { { 64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd, /* TODO: Configuration register */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Spansion",
+ .name = "S25FL129P......1", /* uniform 256 kB sectors */
+ .bustype = BUS_SPI,
+ .manufacture_id = SPANSION_ID,
+ .model_id = SPANSION_S25FL128,
+ .total_size = 16384,
+ .page_size = 256,
+ /* OTP: 506B total, 16B reserved; read 0x4B; write 0x42 */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers = {
+ {
+ .eraseblocks = { {256 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { { 16384 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_bp2_ep_srwd, /* TODO: Configuration register */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25LF020A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF020_REMS,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: No BP2 & 3 */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25LF040A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF040_REMS,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25LF080(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF080_REMS,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_res2,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF512(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF512_REMS,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_d8, /* Supported by SST25VF512A only */
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = spi_block_erase_c7, /* Supported by SST25VF512A only */
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: No BP2 & 3 */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read, /* Fast read (0x0B) supported by SST25VF512A only */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF010(A)",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF010_REMS,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_d8, /* Supported by SST25VF010A only */
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7, /* Supported by SST25VF010A only */
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: No BP2 & 3 */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read, /* Fast read (0x0B) supported by SST25VF010A only */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF020",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF020_REMS,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: No BP2 & 3 */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read, /* only */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF020B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF020B,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: No BP2 & 3 and 2nd SR */
+ .unlock = spi_disable_blockprotect, /* FIXME: 2nd SR */
+ .write = spi_aai_write, /* AAI supported (0xAD) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF040",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF040_REMS,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PR,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_1, /* AAI supported, but opcode is 0xAF */
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF040B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF040B,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25vf040b,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_aai_write, /* AAI supported (0xAD) */
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF040B.REMS",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF040B_REMS,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rems,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25vf040b,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_aai_write,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF020A",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID, /* See flashchips.h */
+ .model_id = SST_SST25WF020A,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_tb_bpl,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF040B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID, /* See flashchips.h */
+ .model_id = SST_SST25WF040B,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_tb_bpl,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual O (0x3B), dual I/O read (0xBB) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF080B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID, /* See flashchips.h */
+ .model_id = SST_SST25WF080B,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_bp2_tb_bpl,
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read, /* Fast read (0x0B), dual O (0x3B), dual I/O read (0xBB) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF080B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF080B,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_aai_write,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF016B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF016B,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25vf016,
+ .unlock = spi_disable_blockprotect,
+ .write = spi_aai_write,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF032B",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF032B,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_aai_write,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25VF064C",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25VF064C,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EWSR,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* TODO: check */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF512",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25WF512,
+ .total_size = 64,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 2} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {1024 * 64, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 64, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: does not have a BP3 */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_aai_write,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF010",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25WF010,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 4} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {1024 * 128, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 128, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: does not have a BP3 */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_aai_write,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF020",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25WF020,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 256, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 256, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: does not have a BP3 */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_aai_write,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF040",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25WF040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 512, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 512, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* FIXME: does not have a BP3 */
+ .unlock = spi_disable_blockprotect_bp2_srwd,
+ .write = spi_aai_write,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST25WF080",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST25WF080,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_EITHER,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ },
+ },
+ .printlock = spi_prettyprint_status_register_sst25, /* *does* have a BP3 but it is useless */
+ .unlock = spi_disable_blockprotect_bp3_srwd,
+ .write = spi_aai_write,
+ .read = spi_chip_read, /* Fast read (0x0B) supported */
+ .voltage = {1650, 1950},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST28SF040A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST28SF040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = 0,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst28sf040.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128, 4096} },
+ .block_erase = erase_sector_28sf040,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_28sf040,
+ }
+ },
+ .unlock = unprotect_28sf040,
+ .write = write_28sf040,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST29EE010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST29EE010,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST29LE010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST29LE010,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST29EE020A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST29EE020A,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST29LE020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST29LE020,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39SF512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39SF512,
+ .total_size = 64,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39SF010A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39SF010,
+ .total_size = 128,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39SF020A",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39SF020,
+ .total_size = 256,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39SF040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39SF040,
+ .total_size = 512,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39VF512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39VF512,
+ .total_size = 64,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39VF010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39VF010,
+ .total_size = 128,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39VF020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39VF020,
+ .total_size = 256,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39VF040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39VF040,
+ .total_size = 512,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST39VF080",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST39VF080,
+ .total_size = 1024,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF002A/B",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF002A,
+ .total_size = 256,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+ }
+ },
+ .printlock = printlock_sst_fwhub,
+ .unlock = unlock_sst_fwhub,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF003A/B",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF003A,
+ .total_size = 384,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 96} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 6} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {384 * 1024, 1} },
+ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+ }
+ },
+ .printlock = printlock_sst_fwhub,
+ .unlock = unlock_sst_fwhub,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ /* Contrary to the data sheet, TBL# on the SST49LF004B affects the top 128kB (instead of 64kB)
+ * and is only honored for 64k block erase, but not 4k sector erase.
+ */
+ .vendor = "SST",
+ .name = "SST49LF004A/B",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF004A,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+ },
+ },
+ .printlock = printlock_sst_fwhub,
+ .unlock = unlock_sst_fwhub,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF004C",
+ .bustype = BUS_FWH,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF004C,
+ .total_size = 512,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_49lfxxxc,
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_1,
+ .unlock = unlock_regspace2_block_eraser_1,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF008A",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF008A,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = NULL, /* AA 55 80 AA 55 10, only in A/A mux mode */
+ }
+ },
+ .printlock = printlock_sst_fwhub,
+ .unlock = unlock_sst_fwhub,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF008C",
+ .bustype = BUS_FWH,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF008C,
+ .total_size = 1024,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = erase_sector_49lfxxxc,
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 15},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_1,
+ .unlock = unlock_regspace2_block_eraser_1,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF016C",
+ .bustype = BUS_FWH,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF016C,
+ .total_size = 2048,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = erase_sector_49lfxxxc,
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 31},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_1,
+ .unlock = unlock_regspace2_block_eraser_1,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF020",
+ .bustype = BUS_LPC,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF020,
+ .total_size = 256,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = NULL,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF020A",
+ .bustype = BUS_LPC,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF020A,
+ .total_size = 256,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PRE,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = NULL,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF040",
+ .bustype = BUS_LPC,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF040,
+ .total_size = 512,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150 ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = NULL,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF040B",
+ .bustype = BUS_LPC, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET | FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 1, /* 150ns */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = NULL,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF080A",
+ .bustype = BUS_LPC, /* A/A Mux */
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF080A,
+ .total_size = 1024,
+ .page_size = 4096,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = NULL,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SST",
+ .name = "SST49LF160C",
+ .bustype = BUS_LPC,
+ .manufacture_id = SST_ID,
+ .model_id = SST_SST49LF160C,
+ .total_size = 2048,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = erase_sector_49lfxxxc,
+ }, {
+ .eraseblocks = {
+ {64 * 1024, 31},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_1,
+ .unlock = unlock_regspace2_block_eraser_1,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29F002B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29F002B,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -X, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29F002T/NT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29F002T,
+ .total_size = 256,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_AAA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4750, 5250}, /* 4.75-5.25V for type -X, others 4.5-5.5V */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29F040B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29F040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* datasheet specifies no timing */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ /* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+ .vendor = "ST",
+ .name = "M29F400BB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29F400BB,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 7},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+ {
+ /* FIXME: this has WORD/BYTE sequences; 2AA for word, 555 for byte */
+ .vendor = "ST",
+ .name = "M29F400BT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29F400BT,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_SHIFTED | FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10, // FIXME: check datasheet. Using the 10 us from probe_m29f400bt
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 7},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29W010B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29W010B,
+ .total_size = 128,
+ .page_size = 16 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {16 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29W040B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29W040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M29W512B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = ST_ID,
+ .model_id = ST_M29W512B,
+ .total_size = 64,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FLW040A",
+ .bustype = BUS_FWH | BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FLW040A,
+ .total_size = 512,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 16}, /* sector */
+ {64 * 1024, 5}, /* block */
+ {4 * 1024, 16}, /* sector */
+ {4 * 1024, 16}, /* sector */
+ },
+ .block_erase = erase_sector_stm50,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .write = write_82802ab,
+ .unlock = unlock_regspace2_uniform_64k,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FLW040B",
+ .bustype = BUS_FWH | BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FLW040B,
+ .total_size = 512,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 16}, /* sector */
+ {4 * 1024, 16}, /* sector */
+ {64 * 1024, 5}, /* block */
+ {4 * 1024, 16}, /* sector */
+ },
+ .block_erase = erase_sector_stm50,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .write = write_82802ab,
+ .unlock = unlock_regspace2_uniform_64k,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FLW080A",
+ .bustype = BUS_FWH | BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FLW080A,
+ .total_size = 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 16}, /* sector */
+ {64 * 1024, 13}, /* block */
+ {4 * 1024, 16}, /* sector */
+ {4 * 1024, 16}, /* sector */
+ },
+ .block_erase = erase_sector_stm50,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FLW080B",
+ .bustype = BUS_FWH | BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FLW080B,
+ .total_size = 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_FIXME,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 16}, /* sector */
+ {4 * 1024, 16}, /* sector */
+ {64 * 1024, 13}, /* block */
+ {4 * 1024, 16}, /* sector */
+ },
+ .block_erase = erase_sector_stm50,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FW002",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FW002,
+ .total_size = 256,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = NULL, /* Only in A/A mux mode */
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FW016",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FW016,
+ .total_size = 2048,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FW040",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FW040,
+ .total_size = 512,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50FW080",
+ .bustype = BUS_FWH, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50FW080,
+ .total_size = 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_OK_PR,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50LPW080",
+ .bustype = BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50LPW080,
+ .total_size = 1024,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "ST",
+ .name = "M50LPW116",
+ .bustype = BUS_LPC, /* A/A Mux */
+ .manufacture_id = ST_ID,
+ .model_id = ST_M50LPW116,
+ .total_size = 2048,
+ .page_size = 0,
+ .feature_bits = FEATURE_REGISTERMAP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_82802ab,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {4 * 1024, 16},
+ {64 * 1024, 30},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_block_82802ab,
+ }
+ },
+ .printlock = printlock_regspace2_block_eraser_0,
+ .unlock = unlock_regspace2_block_eraser_0,
+ .write = write_82802ab,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program & erase */
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51001B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51001B,
+ .total_size = 128,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51001T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51001T,
+ .total_size = 128,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 256} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51002B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51002B,
+ .total_size = 256,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51002T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51002T,
+ .total_size = 256,
+ .page_size = 512,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51004B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51004B,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{F,S,V}29C51004T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C51004T,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{S,V}29C31004B",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C31004B,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "SyncMOS/MoselVitelic",
+ .name = "{S,V}29C31004T",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = SYNCMOS_MVC_ID,
+ .model_id = SM_MVC_29C31004T,
+ .total_size = 512,
+ .page_size = 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {1024, 512} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "TI",
+ .name = "TMS29F002RB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = TI_OLD_ID,
+ .model_id = TI_TMS29F002RB,
+ .total_size = 256,
+ .page_size = 16384, /* Non-uniform sectors */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {16 * 1024, 1},
+ {8 * 1024, 2},
+ {32 * 1024, 1},
+ {64 * 1024, 3},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "TI",
+ .name = "TMS29F002RT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = TI_OLD_ID,
+ .model_id = TI_TMS29F002RT,
+ .total_size = 256,
+ .page_size = 16384, /* Non-uniform sectors */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = TIMING_ZERO, /* Datasheet has no timing info specified */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q40.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q40_V,
+ .total_size = 512,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 756B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256, /* Multi I/O supported */
+ .read = spi_chip_read, /* Fast read (0x0B) and multi I/O supported */
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q80.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q80_V,
+ .total_size = 1024,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q16.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q16_V,
+ .total_size = 2048,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q32.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q32_V,
+ .total_size = 4096,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q64.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q64_V,
+ .total_size = 8192,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q128.V",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q128_V,
+ .total_size = 16384,
+ .page_size = 256,
+ /* supports SFDP */
+ /* OTP: 1024B total, 256B reserved; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 4096} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 512} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 256} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q20.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q20_W,
+ .total_size = 256,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 8} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q40.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q40_W,
+ .total_size = 512,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 16} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q80.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q80_W,
+ .total_size = 1024,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 32} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {1 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q16.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q16_W,
+ .total_size = 2048,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q32.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q32_W,
+ .total_size = 4096,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25Q64.W",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25Q64_W,
+ .total_size = 8192,
+ .page_size = 256,
+ /* OTP: 256B total; read 0x48; write 0x42, erase 0x44, read ID 0x4B */
+ /* QPI enable 0x38, disable 0xFF */
+ .feature_bits = FEATURE_WRSR_WREN | FEATURE_OTP | FEATURE_QPI,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {1700, 1950}, /* Fast read (0x0B) and multi I/O supported */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X10",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X10,
+ .total_size = 128,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 2} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X20",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X20,
+ .total_size = 256,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X40",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X40,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X80",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X80,
+ .total_size = 1024,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 256} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X16",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X16,
+ .total_size = 2048,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 512} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 64} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 32} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {2 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X32",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X32,
+ .total_size = 4096,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 1024} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 128} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W25X64",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = WINBOND_NEX_W25X64,
+ .total_size = 8192,
+ .page_size = 256,
+ .feature_bits = FEATURE_WRSR_WREN,
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 2048} },
+ .block_erase = spi_block_erase_20,
+ }, {
+ .eraseblocks = { {32 * 1024, 256} },
+ .block_erase = spi_block_erase_52,
+ }, {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = spi_block_erase_d8,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_60,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = spi_block_erase_c7,
+ }
+ },
+ .printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
+ .unlock = spi_disable_blockprotect,
+ .write = spi_chip_write_256,
+ .read = spi_chip_read,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29C512A/W29EE512",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W29C512A,
+ .total_size = 64,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29C010(M)/W29C011A/W29EE011/W29EE012-old",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W29C010,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_w29ee011,
+ .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (w29ee011.c) */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ },
+
+ {/* W29EE011, W29EE012, W29C010M, W29C011A do not support probe_jedec according to the datasheet, but it works for newer(?) steppings. */
+ .vendor = "Winbond",
+ .name = "W29C010(M)/W29C011A/W29EE011/W29EE012",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W29C010,
+ .total_size = 128,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10, /* used datasheet for the W29C011A */
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29C020(C)/W29C022",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W29C020,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29C040/P",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W29C040,
+ .total_size = 512,
+ .page_size = 256,
+ .feature_bits = FEATURE_LONG_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL032CB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL032CB,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 63},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL032CT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL032CT,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 63},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL032CH/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL032CHL,
+ .total_size = 4096,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 64} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {4 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL064CB",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL064CB,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {8 * 1024, 8},
+ {64 * 1024, 127},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL064CT",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL064CT,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 127},
+ {8 * 1024, 8},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL064CH/L",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL064CHL,
+ .total_size = 8192,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {8 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W29GL128C",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = AMD_ID, /* WTF: "Industry Standard compatible Manufacturer ID code of 01h" */
+ .model_id = WINBOND_W29GL128CHL,
+ .total_size = 16384,
+ .page_size = 128 * 1024, /* actual page size is 16 */
+ .feature_bits = FEATURE_ADDR_2AA | FEATURE_SHORT_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec_29gl,
+ .probe_timing = TIMING_ZERO,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {128 * 1024, 128} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {16 * 1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ },
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {2700, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39F010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39F010,
+ .total_size = 128,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39f010,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39L010",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39L010,
+ .total_size = 128,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 32} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39l010,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39L020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39L020,
+ .total_size = 256,
+ .page_size = 4 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 64} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 4} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39l020,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39L040",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39L040,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PR,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39l040,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040A",
+ .bustype = BUS_LPC,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040A,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040a,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040B",
+ .bustype = BUS_LPC,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040b,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040C",
+ .bustype = BUS_LPC,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040C,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040c,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040FA",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040FA,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {4 * 1024, 128} },
+ .block_erase = erase_block_jedec,
+ }, {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040fa,
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040FB",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040B,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040fb,
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V040FC",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V040C,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v040fc,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V080A",
+ .bustype = BUS_LPC,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V080A,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v080a,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W49F002U/N",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W49F002U,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {128 * 1024, 1},
+ {96 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W49F020",
+ .bustype = BUS_PARALLEL,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W49F020,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PROBE,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {4500, 5500},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W49V002A",
+ .bustype = BUS_LPC,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W49V002A,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W49V002FA",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W49V002FA,
+ .total_size = 256,
+ .page_size = 128,
+ .feature_bits = FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = {
+ {64 * 1024, 3},
+ {32 * 1024, 1},
+ {8 * 1024, 2},
+ {16 * 1024, 1},
+ },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {256 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600},
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V080FA",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V080FA,
+ .total_size = 1024,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_OK_PREW,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 16} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {1024 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v080fa,
+ .unlock = unlock_regspace2_uniform_64k,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program */
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "W39V080FA (dual mode)",
+ .bustype = BUS_FWH,
+ .manufacture_id = WINBOND_ID,
+ .model_id = WINBOND_W39V080FA_DM,
+ .total_size = 512,
+ .page_size = 64 * 1024,
+ .feature_bits = FEATURE_REGISTERMAP | FEATURE_EITHER_RESET,
+ .tested = TEST_UNTESTED,
+ .probe = probe_jedec,
+ .probe_timing = 10,
+ .block_erasers =
+ {
+ {
+ .eraseblocks = { {64 * 1024, 8} },
+ .block_erase = erase_sector_jedec,
+ }, {
+ .eraseblocks = { {512 * 1024, 1} },
+ .block_erase = erase_chip_block_jedec,
+ }
+ },
+ .printlock = printlock_w39v080fa_dual,
+ .write = write_jedec_1,
+ .read = read_memmapped,
+ .voltage = {3000, 3600}, /* Also has 12V fast program */
+ },
+
+ {
+ .vendor = "Unknown",
+ .name = "SFDP-capable chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = GENERIC_MANUF_ID,
+ .model_id = SFDP_DEVICE_ID,
+ /* We present our own "report this" text hence we do not
+ * want the default "This flash part has status UNTESTED..."
+ * text to be printed. */
+ .tested = TEST_OK_PREW,
+ .probe = probe_spi_sfdp,
+ .unlock = spi_disable_blockprotect, /* is this safe? */
+ .read = spi_chip_read,
+ /* FIXME: some vendor extensions define this */
+ .voltage = {0},
+ /* Everything below will be set by the probing function. */
+ .write = NULL,
+ .total_size = 0,
+ .page_size = 0,
+ .feature_bits = 0,
+ .block_erasers = {},
+ },
+
+ {
+ .vendor = "Programmer",
+ .name = "Opaque flash chip",
+ .bustype = BUS_PROG,
+ .manufacture_id = PROGMANUF_ID,
+ .model_id = PROGDEV_ID,
+ .total_size = 0,
+ .page_size = 256,
+ /* probe is assumed to work, rest will be filled in by probe */
+ .tested = TEST_OK_PROBE,
+ .probe = probe_opaque,
+ /* eraseblock sizes will be set by the probing function */
+ .block_erasers =
+ {
+ {
+ .block_erase = erase_opaque,
+ }
+ },
+ .write = write_opaque,
+ .read = read_opaque,
+ },
+
+ {
+ .vendor = "AMIC",
+ .name = "unknown AMIC SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = AMIC_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid4,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Atmel",
+ .name = "unknown Atmel SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = ATMEL_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Eon",
+ .name = "unknown Eon SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = EON_ID_NOPREFIX,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Macronix",
+ .name = "unknown Macronix SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = MACRONIX_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "PMC",
+ .name = "unknown PMC SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = PMC_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "SST",
+ .name = "unknown SST SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = SST_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "ST",
+ .name = "unknown ST SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = ST_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Sanyo",
+ .name = "unknown Sanyo SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = SANYO_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Winbond",
+ .name = "unknown Winbond (ex Nexcom) SPI chip",
+ .bustype = BUS_SPI,
+ .manufacture_id = WINBOND_NEX_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .probe_timing = TIMING_ZERO,
+ .write = NULL,
+ .read = NULL,
+ },
+
+ {
+ .vendor = "Generic",
+ .name = "unknown SPI chip (RDID)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GENERIC_MANUF_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rdid,
+ .write = NULL,
+ },
+
+ {
+ .vendor = "Generic",
+ .name = "unknown SPI chip (REMS)",
+ .bustype = BUS_SPI,
+ .manufacture_id = GENERIC_MANUF_ID,
+ .model_id = GENERIC_DEVICE_ID,
+ .total_size = 0,
+ .page_size = 256,
+ .tested = TEST_BAD_PREW,
+ .probe = probe_spi_rems,
+ .write = NULL,
+ },
+
+ {0}
+};
+
+const unsigned int flashchips_size = ARRAY_SIZE(flashchips);
diff --git a/flashchips.h b/flashchips.h
new file mode 100644
index 0000000..9ffb30f
--- /dev/null
+++ b/flashchips.h
@@ -0,0 +1,953 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2007 coresystems GmbH <stepan@coresystems.de>
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __FLASHCHIPS_H__
+#define __FLASHCHIPS_H__ 1
+
+/*
+ * Please keep this list sorted alphabetically by manufacturer. The first
+ * entry of each section should be the manufacturer ID, followed by the
+ * list of devices from that manufacturer (sorted by device ID).
+ *
+ * Most LPC/FWH parts (parallel flash) have 8-bit device IDs if there is no
+ * continuation code.
+ * SPI parts have at least 16-bit device IDs if they support RDID.
+ */
+
+#define GENERIC_MANUF_ID 0xFFFF /* Check if there is a vendor ID */
+#define GENERIC_DEVICE_ID 0xFFFF /* Only match the vendor ID */
+#define SFDP_DEVICE_ID 0xFFFE
+#define PROGMANUF_ID 0xFFFE /* dummy ID for opaque chips behind a programmer */
+#define PROGDEV_ID 0x01 /* dummy ID for opaque chips behind a programmer */
+
+#define ALLIANCE_ID 0x52 /* Alliance Semiconductor */
+#define ALLIANCE_AS29F002B 0x34
+#define ALLIANCE_AS29F002T 0xB0
+#define ALLIANCE_AS29F010 0x04
+#define ALLIANCE_AS29F040 0xA4
+#define ALLIANCE_AS29F200B 0x57
+#define ALLIANCE_AS29F200T 0x51
+#define ALLIANCE_AS29LV160B 0x49
+#define ALLIANCE_AS29LV160T 0xCA
+#define ALLIANCE_AS29LV400B 0xBA
+#define ALLIANCE_AS29LV400T 0xB9
+#define ALLIANCE_AS29LV800B 0x5B
+#define ALLIANCE_AS29LV800T 0xDA
+
+#define AMD_ID 0x01 /* AMD */
+#define AMD_AM29DL400BT 0x0C
+#define AMD_AM29DL400BB 0x0F
+#define AMD_AM29DL800BT 0x4A
+#define AMD_AM29DL800BB 0xCB
+#define AMD_AM29F002BB 0x34 /* Same as Am29F002NBB */
+#define AMD_AM29F002BT 0xB0 /* Same as Am29F002NBT */
+#define AMD_AM29F004BB 0x7B
+#define AMD_AM29F004BT 0x77
+#define AMD_AM29F016D 0xAD
+#define AMD_AM29F010 0x20 /* Same as Am29F010A and Am29F010B */
+#define AMD_AM29F040 0xA4 /* Same as AM29F040B */
+#define AMD_AM29F080 0xD5 /* Same as Am29F080B */
+#define AMD_AM29F200BB 0x57
+#define AMD_AM29F200BT 0x51
+#define AMD_AM29F400BB 0xAB
+#define AMD_AM29F400BT 0x23
+#define AMD_AM29F800BB 0x58
+#define AMD_AM29F800BT 0xD6
+#define AMD_AM29LV001BB 0x6D
+#define AMD_AM29LV001BT 0xED
+#define AMD_AM29LV010B 0x6E /* 1Mb, uniform */
+#define AMD_AM29LV002BB 0xC2
+#define AMD_AM29LV002BT 0x40
+#define AMD_AM29LV004BB 0xB6
+#define AMD_AM29LV004BT 0xB5
+#define AMD_AM29LV008BB 0x37
+#define AMD_AM29LV008BT 0x3E
+#define AMD_AM29LV040B 0x4F
+#define AMD_AM29LV080B 0x38 /* Same as Am29LV081B */
+#define AMD_AM29LV200BB 0xBF
+#define AMD_AM29LV200BT 0x3B
+#define AMD_AM29LV800BB 0x5B /* Same as Am29LV800DB */
+#define AMD_AM29LV400BT 0xB9
+#define AMD_AM29LV400BB 0xBA
+#define AMD_AM29LV800BT 0xDA /* Same as Am29LV800DT */
+
+#define AMIC_ID 0x7F37 /* AMIC */
+#define AMIC_ID_NOPREFIX 0x37 /* AMIC */
+#define AMIC_A25L05PT 0x2020
+#define AMIC_A25L05PU 0x2010
+#define AMIC_A25L10PT 0x2021
+#define AMIC_A25L10PU 0x2011
+#define AMIC_A25L20PT 0x2022
+#define AMIC_A25L20PU 0x2012
+#define AMIC_A25L40PT 0x2013 /* Datasheet says T and U have
+ same device ID. Confirmed by
+ hardware testing. */
+#define AMIC_A25L40PU 0x2013
+#define AMIC_A25L80P 0x2014 /* Seems that no A25L80PT exists */
+#define AMIC_A25L16PT 0x2025
+#define AMIC_A25L16PU 0x2015
+#define AMIC_A25L512 0x3010
+#define AMIC_A25L010 0x3011
+#define AMIC_A25L020 0x3012
+#define AMIC_A25L040 0x3013
+#define AMIC_A25L080 0x3014
+#define AMIC_A25L016 0x3015
+#define AMIC_A25L032 0x3016
+#define AMIC_A25LQ16 0x4015
+#define AMIC_A25LQ032 0x4016 /* Same as A25LQ32A, but the latter supports SFDP */
+#define AMIC_A25LQ64 0x4017
+#define AMIC_A29002B 0x0d
+#define AMIC_A29002T 0x8C /* Same as A290021T */
+#define AMIC_A29040B 0x86
+#define AMIC_A29400T 0xB0 /* Same as 294001T */
+#define AMIC_A29400U 0x31 /* Same as A294001U */
+#define AMIC_A29800T 0x0E
+#define AMIC_A29800U 0x8F
+#define AMIC_A29L004T 0x34 /* Same as A29L400T */
+#define AMIC_A29L004U 0xB5 /* Same as A29L400U */
+#define AMIC_A29L008T 0x1A /* Same as A29L800T */
+#define AMIC_A29L008U 0x9B /* Same as A29L800U */
+#define AMIC_A29L040 0x92
+#define AMIC_A49LF040A 0x9d
+
+#define ATMEL_ID 0x1F /* Atmel (now used by Adesto) */
+#define ATMEL_AT25DF021 0x4300
+#define ATMEL_AT25DF041A 0x4401
+#define ATMEL_AT25DF081 0x4502 /* EDI 0x00. AT25DL081 has same ID + EDI 0x0100 */
+#define ATMEL_AT25DF081A 0x4501 /* Yes, 81A has a lower number than 81 */
+#define ATMEL_AT25DF161 0x4602
+#define ATMEL_AT25DF321 0x4700 /* Same as 26DF321 */
+#define ATMEL_AT25DF321A 0x4701
+#define ATMEL_AT25DF641 0x4800
+#define ATMEL_AT25DL161 0x4603 /* EDI 0x0100 */
+#define ATMEL_AT25DQ161 0x8600 /* EDI 0x0100 */
+#define ATMEL_AT25DQ321 0x8700 /* EDI 0x0100 */
+#define ATMEL_AT25F512 0x60 /* Needs AT25F_RDID. ID from PCN and actual HW. Seems to be a relabeled AT25F1024. */
+#define ATMEL_AT25F512A 0x65 /* Needs AT25F_RDID */
+#define ATMEL_AT25F512B 0x6500
+#define ATMEL_AT25F1024 0x60 /* Needs AT25F_RDID */
+#define ATMEL_AT25F2048 0x63 /* Needs AT25F_RDID */
+#define ATMEL_AT25F4096 0x64 /* Needs AT25F_RDID */
+#define ATMEL_AT25FS010 0x6601
+#define ATMEL_AT25FS040 0x6604
+#define ATMEL_AT26DF041 0x4400
+#define ATMEL_AT26DF081 0x4500 /* guessed, no datasheet available */
+#define ATMEL_AT26DF081A 0x4501
+#define ATMEL_AT26DF161 0x4600
+#define ATMEL_AT26DF161A 0x4601
+#define ATMEL_AT26DF321 0x4700 /* Same as 25DF321 */
+#define ATMEL_AT26F004 0x0400
+#define ATMEL_AT29LV512 0x3D
+#define ATMEL_AT29LV010A 0x35 /* Same as AT29BV010A, the latter works down to 2.7V */
+#define ATMEL_AT29LV020 0xBA
+#define ATMEL_AT29BV040A 0xC4
+#define ATMEL_AT29C040A 0xA4
+#define ATMEL_AT29C010A 0xD5
+#define ATMEL_AT29C020 0xDA
+#define ATMEL_AT29C512 0x5D
+#define ATMEL_AT45BR3214B /* No ID available */
+#define ATMEL_AT45CS1282 0x2920
+#define ATMEL_AT45D011 /* No ID available */
+#define ATMEL_AT45D021A /* No ID available */
+#define ATMEL_AT45D041A /* No ID available */
+#define ATMEL_AT45D081A /* No ID available */
+#define ATMEL_AT45D161 /* No ID available */
+#define ATMEL_AT45DB011 /* No ID (opcode) available for AT45DB011, AT45DB011B */
+#define ATMEL_AT45DB011D 0x2200
+#define ATMEL_AT45DB021 /* No ID (opcode) available for AT45DB021, AT45DB021A, AT45DB021B */
+#define ATMEL_AT45DB021D 0x2300
+#define ATMEL_AT45DB021E /* same as above but with EDI 0x0100 */
+#define ATMEL_AT45DB041 /* No ID (opcode) available for AT45DB041, AT45DB041A, AT45DB041B */
+#define ATMEL_AT45DB041D 0x2400
+#define ATMEL_AT45DB041E /* same as above but with EDI 0x0100 */
+#define ATMEL_AT45DB081 /* No ID (opcode) available for AT45DB081, AT45DB081A, AT45DB081B */
+#define ATMEL_AT45DB081D 0x2500
+#define ATMEL_AT45DB081E /* same as above but with EDI 0x0100 */
+#define ATMEL_AT45DB161 /* No ID (opcode) available for AT45DB161, AT45DB161B */
+#define ATMEL_AT45DB161D 0x2600
+#define ATMEL_AT45DB161E /* same as above but with EDI 0x0100 */
+#define ATMEL_AT45DB321 /* No ID (opcode) available for AT45DB321, AT45DB321B */
+#define ATMEL_AT45DB321C 0x2700
+#define ATMEL_AT45DB321E /* same as above but with EDI 0x0100 */
+#define ATMEL_AT45DB321D 0x2701 /* Buggy data sheet */
+#define ATMEL_AT45DB642 /* No ID (opcode) available for AT45DB642 */
+#define ATMEL_AT45DB642D 0x2800
+#define ATMEL_AT49BV512 0x03 /* Same as AT49F512 */
+#define ATMEL_AT49F001N 0x05 /* Same as AT49F001 */
+#define ATMEL_AT49F001NT 0x04 /* Same as AT49F001T */
+#define ATMEL_AT49F002N 0x07 /* for AT49F002(N) */
+#define ATMEL_AT49LH002 0xE9
+#define ATMEL_AT49LH00B4 0xED
+#define ATMEL_AT49LH004 0xEE
+#define ATMEL_AT49F002NT 0x08 /* for AT49F002(N)T */
+#define ATMEL_AT49F010 0x17 /* Same as AT49HF010 (some erroneous datasheets say 0x87), AT49BV010, AT49HBV010, AT49HLV010 */
+#define ATMEL_AT49F020 0x0B
+#define ATMEL_AT49F040 0x13
+#define ATMEL_AT49F080 0x23
+#define ATMEL_AT49F080T 0x27
+
+/* Bright Microelectronics has the same manufacturer ID as Hyundai... */
+#define BRIGHT_ID 0xAD /* Bright Microelectronics */
+#define BRIGHT_BM29F040 0x40
+#define BRIGHT_BM29F400B 0xAB
+#define BRIGHT_BM29F400T 0xAD
+
+#define CATALYST_ID 0x31 /* Catalyst */
+#define CATALYST_CAT28F512 0xB8
+
+#define ESMT_ID 0x8C /* Elite Semiconductor Memory Technology (ESMT) / EFST Elite Flash Storage */
+#define ESMT_F25L008A 0x2014
+#define ESMT_F25L32PA 0x2016
+#define ESMT_F25D08QA 0x2534
+#define ESMT_F25L16QA2S 0x4015
+#define ESMT_F25L32QA 0x4016
+#define ESMT_F25L32QA2S 0x4116
+#define ESMT_F25L64QA 0x4117
+#define ESMT_F25L128QA 0x4118
+#define ESMT_F49B002UA 0x00
+
+/*
+ * EN25 chips are SPI, first byte of device ID is memory type,
+ * second byte of device ID is log(bitsize)-9.
+ * Vendor and device ID of EN29 series are both prefixed with 0x7F, which
+ * is the continuation code for IDs in bank 2.
+ * Vendor ID of EN25 series is NOT prefixed with 0x7F, this results in
+ * a collision with Mitsubishi. Mitsubishi once manufactured flash chips.
+ * Let's hope they are not manufacturing SPI flash chips as well.
+ */
+#define EON_ID 0x7F1C /* EON Silicon Devices */
+#define EON_ID_NOPREFIX 0x1C /* EON, missing 0x7F prefix */
+#define EON_EN25B05 0x2010 /* Same as EN25P05, can be distinguished by RES/REMS: */
+#define EON_EN25P05 0x05
+#define EON_EN25B05T 0x25
+#define EON_EN25B05B 0x95
+#define EON_EN25B10 0x2011 /* Same as EN25P10, can be distinguished by RES/REMS: */
+#define EON_EN25P10 0x10
+#define EON_EN25B10T 0x40
+#define EON_EN25B10B 0x30
+#define EON_EN25B20 0x2012 /* Same as EN25P20, can be distinguished by RES/REMS: */
+#define EON_EN25P20 0x11
+#define EON_EN25B20T 0x41
+#define EON_EN25B20B 0x31
+#define EON_EN25B40 0x2013 /* Same as EN25P40, can be distinguished by RES/REMS: */
+#define EON_EN25P40 0x12
+#define EON_EN25B40T 0x42
+#define EON_EN25B40B 0x32
+#define EON_EN25B80 0x2014 /* Same as EN25P80, can be distinguished by RES/REMS: */
+#define EON_EN25P80 0x13
+#define EON_EN25B80T 0x43
+#define EON_EN25B80B 0x33
+#define EON_EN25B16 0x2015 /* Same as EN25P16, can be distinguished by RES/REMS: */
+#define EON_EN25P16 0x14
+#define EON_EN25B16T 0x44
+#define EON_EN25B16B 0x34
+#define EON_EN25B32 0x2016 /* Same as EN25P32, can be distinguished by RES/REMS: */
+#define EON_EN25P32 0x15
+#define EON_EN25B32T 0x45
+#define EON_EN25B32B 0x35
+#define EON_EN25B64 0x2017 /* Same as EN25P64, can be distinguished by RES/REMS: */
+#define EON_EN25P64 0x16
+#define EON_EN25B64T 0x46
+#define EON_EN25B64B 0x36
+#define EON_EN25F05 0x3110
+#define EON_EN25F10 0x3111
+#define EON_EN25F20 0x3112
+#define EON_EN25F40 0x3113
+#define EON_EN25F80 0x3114
+#define EON_EN25F16 0x3115
+#define EON_EN25F32 0x3116
+#define EON_EN25F64 0x3117
+#define EON_EN25Q40 0x3013
+#define EON_EN25Q80 0x3014
+#define EON_EN25Q16 0x3015 /* Same as EN25D16 */
+#define EON_EN25Q32 0x3016 /* Same as EN25Q32A and EN25Q32B */
+#define EON_EN25Q64 0x3017
+#define EON_EN25Q128 0x3018
+#define EON_EN25QH16 0x7015
+#define EON_EN25QH32 0x7016
+#define EON_EN25QH64 0x7017
+#define EON_EN25QH128 0x7018
+#define EON_EN25QH256 0x7019
+#define EON_EN25S10 0x3811
+#define EON_EN25S20 0x3812
+#define EON_EN25S40 0x3813
+#define EON_EN25S80 0x3814
+#define EON_EN25S16 0x3815
+#define EON_EN25S32 0x3816
+#define EON_EN25S64 0x3817
+#define EON_EN25T80 0x5114
+#define EON_EN25T16 0x5115
+#define EON_EN29F512 0x7F21
+#define EON_EN29F010 0x20
+#define EON_EN29F040A 0x7F04
+#define EON_EN29LV010 0x7F6E
+#define EON_EN29LV040 0x4F /* Same as EN29LV040A */
+#define EON_EN29LV640B 0xCB
+#define EON_EN29LV640T 0xC9
+#define EON_EN29LV640U 0x7E
+#define EON_EN29F002T 0x7F92 /* Same as EN29F002A */
+#define EON_EN29F002B 0x7F97 /* Same as EN29F002AN */
+#define EON_EN29GL064HL 0x7E0C01 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define EON_EN29GL064T 0x7E1001 /* Same ID as EN29GL064AT */
+#define EON_EN29GL064B 0x7E1000 /* Same ID as EN29GL064AB */
+#define EON_EN29GL128HL 0x7F2101 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define EON_EN29GL256HL 0x7F2201 /* Uniform Sectors, WP protects Top OR Bottom sector */
+
+#define EXCEL_ID 0x7F7F7F7F4A /* Excel Semiconductor Inc. (ESI) resides in bank 5 */
+#define EXCEL_ID_NOPREFIX 0x4A /* ESI, missing 0x7F prefix */
+#define EXCEL_ES25P40 0x2013
+#define EXCEL_ES25P80 0x2014
+#define EXCEL_ES25P16 0x2015
+
+#define FIDELIX_ID 0xF8 /* Fidelix */
+#define FIDELIX_FM25M16 0x4215
+#define FIDELIX_FM25M32 0x4216
+#define FIDELIX_FM25M64 0x4217
+#define FIDELIX_FM25Q08 0x3214
+#define FIDELIX_FM25Q16 0x3215 /* Same as FM25S16 (which is apparently single I/O only) */
+#define FIDELIX_FM25Q32 0x3216
+#define FIDELIX_FM25Q64 0x3217
+
+#define FUJITSU_ID 0x04 /* Fujitsu */
+#define FUJITSU_MBM29DL400BC 0x0F
+#define FUJITSU_MBM29DL400TC 0x0C
+#define FUJITSU_MBM29DL800BA 0xCB
+#define FUJITSU_MBM29DL800TA 0x4A
+#define FUJITSU_MBM29F002BC 0x34
+#define FUJITSU_MBM29F002TC 0xB0
+#define FUJITSU_MBM29F004BC 0x7B
+#define FUJITSU_MBM29F004TC 0x77
+#define FUJITSU_MBM29F040C 0xA4
+#define FUJITSU_MBM29F080A 0xD5
+#define FUJITSU_MBM29F200BC 0x57
+#define FUJITSU_MBM29F200TC 0x51
+#define FUJITSU_MBM29F400BC 0xAB
+#define FUJITSU_MBM29F400TC 0x23
+#define FUJITSU_MBM29F800BA 0x58
+#define FUJITSU_MBM29F800TA 0xD6
+#define FUJITSU_MBM29LV002BC 0xC2
+#define FUJITSU_MBM29LV002TC 0x40
+#define FUJITSU_MBM29LV004BC 0xB6
+#define FUJITSU_MBM29LV004TC 0xB5
+#define FUJITSU_MBM29LV008BA 0x37
+#define FUJITSU_MBM29LV008TA 0x3E
+#define FUJITSU_MBM29LV080A 0x38
+#define FUJITSU_MBM29LV200BC 0xBF
+#define FUJITSU_MBM29LV200TC 0x3B
+#define FUJITSU_MBM29LV400BC 0xBA
+#define FUJITSU_MBM29LV400TC 0xB9
+#define FUJITSU_MBM29LV800BA 0x5B /* Same as MBM29LV800BE */
+#define FUJITSU_MBM29LV800TA 0xDA /* Same as MBM29LV800TE */
+#define FUJITSU_MBM29LV160BE 0x49 /* 16 b mode 0x2249 */
+#define FUJITSU_MBM29LV160TE 0xC4 /* 16 b mode 0x22C4 */
+
+#define GIGADEVICE_ID 0xC8 /* GigaDevice */
+#define GIGADEVICE_GD25T80 0x3114
+#define GIGADEVICE_GD25Q512 0x4010
+#define GIGADEVICE_GD25Q10 0x4011
+#define GIGADEVICE_GD25Q20 0x4012 /* Same as GD25QB */
+#define GIGADEVICE_GD25Q40 0x4013 /* Same as GD25QB */
+#define GIGADEVICE_GD25Q80 0x4014 /* Same as GD25Q80B (which has OTP) */
+#define GIGADEVICE_GD25Q16 0x4015 /* Same as GD25Q16B (which has OTP) */
+#define GIGADEVICE_GD25Q32 0x4016 /* Same as GD25Q32B */
+#define GIGADEVICE_GD25Q64 0x4017 /* Same as GD25Q64B */
+#define GIGADEVICE_GD25Q128 0x4018 /* GD25Q128B and GD25Q128C only, can be distinguished by SFDP */
+#define GIGADEVICE_GD25VQ21B 0x4212
+#define GIGADEVICE_GD25VQ41B 0x4213 /* Same as GD25VQ40C, can be distinguished by SFDP */
+#define GIGADEVICE_GD25VQ80C 0x4214
+#define GIGADEVICE_GD25VQ16C 0x4215
+#define GIGADEVICE_GD25LQ40 0x6013
+#define GIGADEVICE_GD25LQ80 0x6014
+#define GIGADEVICE_GD25LQ16 0x6015
+#define GIGADEVICE_GD25LQ32 0x6016
+#define GIGADEVICE_GD25LQ64 0x6017 /* Same as GD25LQ64B (which is faster) */
+#define GIGADEVICE_GD25LQ128 0x6018
+#define GIGADEVICE_GD29GL064CAB 0x7E0601
+
+#define HYUNDAI_ID 0xAD /* Hyundai */
+#define HYUNDAI_HY29F400T 0x23 /* Same as HY29F400AT */
+#define HYUNDAI_HY29F800B 0x58 /* Same as HY29F800AB */
+#define HYUNDAI_HY29LV800B 0x5B
+#define HYUNDAI_HY29F040A 0xA4
+#define HYUNDAI_HY29F400B 0xAB /* Same as HY29F400AB */
+#define HYUNDAI_HY29F002B 0x34
+#define HYUNDAI_HY29F002T 0xB0
+#define HYUNDAI_HY29LV400T 0xB9
+#define HYUNDAI_HY29LV400B 0xBA
+#define HYUNDAI_HY29F080 0xD5
+#define HYUNDAI_HY29F800T 0xD6 /* Same as HY29F800AT */
+#define HYUNDAI_HY29LV800T 0xDA
+
+#define IMT_ID 0x7F1F /* Integrated Memory Technologies */
+#define IMT_IM29F004B 0xAE
+#define IMT_IM29F004T 0xAF
+
+#define INTEL_ID 0x89 /* Intel */
+#define INTEL_28F320J5 0x14
+#define INTEL_28F640J5 0x15
+#define INTEL_28F320J3 0x16
+#define INTEL_28F640J3 0x17
+#define INTEL_28F128J3 0x18
+#define INTEL_28F256J3 0x1D
+#define INTEL_28F400T 0x70 /* 28F400BV/BX/CE/CV-T */
+#define INTEL_28F400B 0x71 /* 28F400BV/BX/CE/CV-B */
+#define INTEL_28F200T 0x74 /* 28F200BL/BV/BX/CV-T */
+#define INTEL_28F200B 0x75 /* 28F200BL/BV/BX/CV-B */
+#define INTEL_28F004T 0x78 /* 28F004B5/BE/BV/BX-T */
+#define INTEL_28F004B 0x79 /* 28F004B5/BE/BV/BX-B */
+#define INTEL_28F002T 0x7C /* 28F002BC/BL/BV/BX-T */
+#define INTEL_28F002B 0x7D /* 28F002BL/BV/BX-B */
+#define INTEL_28F001T 0x94 /* 28F001BN/BX-T */
+#define INTEL_28F001B 0x95 /* 28F001BN/BX-B */
+#define INTEL_28F008T 0x98 /* 28F008BE/BV-T */
+#define INTEL_28F008B 0x99 /* 28F008BE/BV-B */
+#define INTEL_28F800T 0x9C /* 28F800B5/BV/CE/CV-T */
+#define INTEL_28F800B 0x9D /* 28F800B5/BV/CE/CV-B */
+#define INTEL_28F016SV 0xA0 /* 28F016SA/SV */
+#define INTEL_28F008SA 0xA2
+#define INTEL_28F008S3 0xA6 /* 28F008S3/S5/SC */
+#define INTEL_28F004S3 0xA7 /* 28F008S3/S5/SC */
+#define INTEL_28F016XS 0xA8
+#define INTEL_28F016S3 0xAA /* 28F016S3/S5/SC */
+#define INTEL_82802AC 0xAC
+#define INTEL_82802AB 0xAD
+#define INTEL_28F010 0xB4
+#define INTEL_28F512 0xB8
+#define INTEL_28F256A 0xB9
+#define INTEL_28F020 0xBD
+#define INTEL_28F016B3T 0xD0 /* 28F016B3-T */
+#define INTEL_28F016B3B 0xD1 /* 28F016B3-B */
+#define INTEL_28F008B3T 0xD2 /* 28F008B3-T */
+#define INTEL_28F008B3B 0xD3 /* 28F008B3-B */
+#define INTEL_28F004B3T 0xD4 /* 28F004B3-T */
+#define INTEL_28F004B3B 0xD5 /* 28F004B3-B */
+#define INTEL_25F160S33B8 0x8911 /* Same as 25F016S33B8 */
+#define INTEL_25F320S33B8 0x8912
+#define INTEL_25F640S33B8 0x8913
+#define INTEL_25F160S33T8 0x8915 /* Same as 25F016S33T8 */
+#define INTEL_25F320S33T8 0x8916
+#define INTEL_25F640S33T8 0x8917
+
+#define SHARP_LH28F008SA 0xA2 /* Sharp chip, Intel Vendor ID */
+#define SHARP_LH28F008SC 0xA6 /* Sharp chip, Intel Vendor ID */
+
+#define ISSI_ID 0xD5 /* ISSI Integrated Silicon Solutions, see also PMC. */
+#define ISSI_PMC_IS29GL032B 0xF9
+#define ISSI_PMC_IS29GL032T 0xF6
+#define ISSI_PMC_IS29GL064B 0x7E1000
+#define ISSI_PMC_IS29GL064T 0x7E1001
+#define ISSI_PMC_IS29GL064HL 0x7E0C01
+#define ISSI_PMC_IS29GL128HL 0x7E2101
+#define ISSI_PMC_IS29GL256HL 0x7E2201
+
+#define MACRONIX_ID 0xC2 /* Macronix (MX) */
+/* Mask ROMs */
+#define MACRONIX_MX23L1654 0x0515
+#define MACRONIX_MX23L3254 0x0516
+#define MACRONIX_MX23L6454 0x0517
+#define MACRONIX_MX23L12854 0x0518
+/* MX25 chips are SPI, first byte of device ID is memory type,
+ * second byte of device ID is log(bitsize)-9.
+ * Generalplus SPI chips seem to be compatible with Macronix
+ * and use the same set of IDs. */
+#define MACRONIX_MX25L512 0x2010 /* Same as MX25L512E, MX25V512, MX25V512C */
+#define MACRONIX_MX25L1005 0x2011 /* Same as MX25L1005C, MX25L1006E */
+#define MACRONIX_MX25L2005 0x2012 /* Same as MX25L2005C, MX25L2006E */
+#define MACRONIX_MX25L4005 0x2013 /* Same as MX25L4005A, MX25L4005C, MX25L4006E */
+#define MACRONIX_MX25L8005 0x2014 /* Same as MX25V8005, MX25L8006E, MX25L8008E, FIXME: MX25L8073E (4k 0x20) */
+#define MACRONIX_MX25L1605 0x2015 /* MX25L1605 (64k 0x20); MX25L1605A/MX25L1606E/MX25L1608E (4k 0x20, 64k 0x52); MX25L1605D/MX25L1608D/MX25L1673E (4k 0x20) */
+#define MACRONIX_MX25L3205 0x2016 /* MX25L3205, MX25L3205A (64k 0x20); MX25L3205D/MX25L3208D (4k 0x20); MX25L3206E/MX25L3208E (4k 0x20, 64k 0x52); MX25L3273E (4k 0x20, 32k 0x52) */
+#define MACRONIX_MX25L6405 0x2017 /* MX25L6405, MX25L6405D (64k 0x20); MX25L6406E/MX25L6408E (4k 0x20); MX25L6436E/MX25L6445E/MX25L6465E/MX25L6473E (4k 0x20, 32k 0x52) */
+#define MACRONIX_MX25L12805D 0x2018 /* MX25L12805D (no 32k); MX25L12865E, MX25L12835F, MX25L12845E (32k 0x52) */
+#define MACRONIX_MX25L25635F 0x2019 /* Same as MX25L25639F, but the latter seems to not support REMS */
+#define MACRONIX_MX25L1635D 0x2415
+#define MACRONIX_MX25L1635E 0x2515 /* MX25L1635{E} */
+#define MACRONIX_MX25U1635E 0x2535
+#define MACRONIX_MX25U3235E 0x2536 /* Same as MX25U6435F */
+#define MACRONIX_MX25U6435E 0x2537 /* Same as MX25U6435F */
+#define MACRONIX_MX25U12835E 0x2538 /* Same as MX25U12835F */
+#define MACRONIX_MX25U25635F 0x2539
+#define MACRONIX_MX25L3235D 0x5E16 /* MX25L3225D/MX25L3235D/MX25L3237D */
+#define MACRONIX_MX25L6495F 0x9517
+
+#define MACRONIX_MX29F001B 0x19
+#define MACRONIX_MX29F001T 0x18
+#define MACRONIX_MX29F002B 0x34 /* Same as MX29F002NB; N has reset pin n/c. */
+#define MACRONIX_MX29F002T 0xB0 /* Same as MX29F002NT; N has reset pin n/c. */
+#define MACRONIX_MX29F004B 0x46
+#define MACRONIX_MX29F004T 0x45
+#define MACRONIX_MX29F022B 0x37 /* Same as MX29F022NB */
+#define MACRONIX_MX29F022T 0x36 /* Same as MX29F022NT */
+#define MACRONIX_MX29F040 0xA4 /* Same as MX29F040C */
+#define MACRONIX_MX29F080 0xD5
+#define MACRONIX_MX29F200B 0x57 /* Same as MX29F200CB */
+#define MACRONIX_MX29F200T 0x51 /* Same as MX29F200CT */
+#define MACRONIX_MX29F400B 0xAB /* Same as MX29F400CB */
+#define MACRONIX_MX29F400T 0x23 /* Same as MX29F400CT */
+#define MACRONIX_MX29F800B 0x58
+#define MACRONIX_MX29F800T 0xD6
+#define MACRONIX_MX29GL320EB 0x7E1A00
+#define MACRONIX_MX29GL320ET 0x7E1A01
+#define MACRONIX_MX29GL320EHL 0x7E1D00
+#define MACRONIX_MX29GL640EB 0x7E1000
+#define MACRONIX_MX29GL640ET 0x7E1001
+#define MACRONIX_MX29GL640EHL 0x7E0C01
+#define MACRONIX_MX29GL128F 0x7E2101 /* Same as MX29GL128E */
+#define MACRONIX_MX29GL256F 0x7E2201 /* Same as MX29GL256E */
+#define MACRONIX_MX29GL512F 0x7E2301
+#define MACRONIX_MX68GL1G0F 0x7E2801
+#define MACRONIX_MX29LV002CB 0x5A
+#define MACRONIX_MX29LV002CT 0x59
+#define MACRONIX_MX29LV004B 0xB6 /* Same as MX29LV004CB */
+#define MACRONIX_MX29LV004T 0xB5 /* Same as MX29LV004CT */
+#define MACRONIX_MX29LV008B 0x37 /* Same as MX29LV008CB */
+#define MACRONIX_MX29LV008T 0x3E /* Same as MX29LV008CT */
+#define MACRONIX_MX29LV040 0x4F /* Same as MX29LV040C */
+#define MACRONIX_MX29LV081 0x38
+#define MACRONIX_MX29LV128DB 0x7A
+#define MACRONIX_MX29LV128DT 0x7E
+#define MACRONIX_MX29LV160DB 0x49 /* Same as MX29LV161DB/MX29LV160CB */
+#define MACRONIX_MX29LV160DT 0xC4 /* Same as MX29LV161DT/MX29LV160CT */
+#define MACRONIX_MX29LV320DB 0xA8 /* Same as MX29LV321DB */
+#define MACRONIX_MX29LV320DT 0xA7 /* Same as MX29LV321DT */
+#define MACRONIX_MX29LV400B 0xBA /* Same as MX29LV400CB */
+#define MACRONIX_MX29LV400T 0xB9 /* Same as MX29LV400CT */
+#define MACRONIX_MX29LV640DB 0xCB /* Same as MX29LV640EB */
+#define MACRONIX_MX29LV640DT 0xC9 /* Same as MX29LV640ET */
+#define MACRONIX_MX29LV800B 0x5B /* Same as MX29LV800CB */
+#define MACRONIX_MX29LV800T 0xDA /* Same as MX29LV800CT */
+#define MACRONIX_MX29SL402CB 0xF1
+#define MACRONIX_MX29SL402CT 0x70
+#define MACRONIX_MX29SL800CB 0x6B /* Same as MX29SL802CB */
+#define MACRONIX_MX29SL800CT 0xEA /* Same as MX29SL802CT */
+
+/* Nantronics Semiconductors is listed in JEP106AJ in bank 7, so it should have 6 continuation codes in front
+ * of the manufacturer ID of 0xD5. http://www.nantronicssemi.com */
+#define NANTRONICS_ID 0x7F7F7F7F7F7FD5 /* Nantronics */
+#define NANTRONICS_ID_NOPREFIX 0xD5 /* Nantronics, missing prefix */
+#define NANTRONICS_N25S10 0x3011
+#define NANTRONICS_N25S20 0x3012
+#define NANTRONICS_N25S40 0x3013
+#define NANTRONICS_N25S80 0x3014
+#define NANTRONICS_N25S16 0x3015
+
+/*
+ * Programmable Micro Corp is listed in JEP106W in bank 2, so it should
+ * have a 0x7F continuation code prefix.
+ * Apparently PMC was renamed to "Chingis Technology Corporation" http://www.chingistek.com which is now a
+ * subsidiary of ISSI. They continue to use the PMC manufacturer ID (instead of ISSI's) nevertheless, even for
+ * new chips with IS* model numbers.
+ */
+#define PMC_ID 0x7F9D /* PMC */
+#define PMC_ID_NOPREFIX 0x9D /* PMC, missing 0x7F prefix */
+#define PMC_PM25LD256C 0x2F
+#define PMC_PM25LD512 0x20 /* Same as Pm25LD512C, but the latter has more locking options. */
+#define PMC_PM25LD010 0x21 /* Same as Pm25LD010C, but the latter has more locking options. */
+#define PMC_PM25LD020 0x22 /* Same as Pm25LD020C, but the latter has more locking options. */
+#define PMC_PM25LQ020 0x42
+#define PMC_PM25LQ040 0x43
+#define PMC_PM25LQ080 0x44
+#define PMC_PM25LQ016 0x45
+#define PMC_PM25LQ032C 0x46
+#define PMC_PM25LV512 0x7B /* Same as Pm25LV512A */
+#define PMC_PM25LV010 0x7C /* Same as Pm25LV010A, but the former does not support RDID but RES3 only. */
+#define PMC_PM25LV020 0x7D
+#define PMC_PM25LV040 0x7E /* Same as PM25LD040(C), but the latter supports more features. */
+#define PMC_PM25LV080B 0x13
+#define PMC_PM25LV016B 0x14
+#define PMC_PM29F002T 0x1D
+#define PMC_PM29F002B 0x2D
+#define PMC_PM39LV512 0x1B /* Same as IS39LV512 */
+#define PMC_PM39F010 0x1C /* Same as Pm39LV010, IS39LV010 */
+#define PMC_PM39LV020 0x3D
+#define PMC_PM39LV040 0x3E /* Same as IS39LV040 */
+#define PMC_PM39F020 0x4D
+#define PMC_PM39F040 0x4E
+#define PMC_PM49FL002 0x6D
+#define PMC_PM49FL004 0x6E
+
+/*
+ * The Sanyo chip found so far uses SPI, first byte is manufacture code,
+ * second byte is the device code,
+ * third byte is a dummy byte.
+ */
+#define SANYO_ID 0x62 /* Sanyo */
+#define SANYO_LE25FW203A 0x1600
+#define SANYO_LE25FW403A 0x1100
+#define SANYO_LE25FW106 0x15
+#define SANYO_LE25FW406 0x07 /* RES2 */
+#define SANYO_LE25FW418A 0x10 /* RES2 and some weird 1 byte RDID variant */
+#define SANYO_LE25FW406A 0x1A /* RES2, no datasheet */
+#define SANYO_LE25FU406B 0x1E /* LE25FW418A without HD_READ mode option variant */
+#define SANYO_LE25FU406C 0x0613 /* Also known as LE25U40CMC apparently */
+#define SANYO_LE25FW806 0x26 /* RES2 and some weird 1 byte RDID variant */
+#define SANYO_LE25FW808 0x20 /* RES2 and some weird 1 byte RDID variant */
+
+#define SHARP_ID 0xB0 /* Sharp */
+#define SHARP_LH28F008BJ__PT 0xEC
+#define SHARP_LH28F008BJ__PB 0xED
+#define SHARP_LH28F800BV__BTL 0x4B
+#define SHARP_LH28F800BV__BV 0x4D
+#define SHARP_LH28F800BV__TV 0x4C
+#define SHARP_LHF00L02 0xC9 /* Same as LHF00L06/LHF00L07 */
+#define SHARP_LHF00L04 0xCF /* Same as LHF00L03/LHF00L05 */
+
+/* Spansion was previously a joint venture of AMD and Fujitsu. */
+#define SPANSION_ID 0x01 /* Spansion, same ID as AMD */
+/* S25 chips are SPI. The first device ID byte is memory type and
+ * the second device ID byte is memory capacity. */
+#define SPANSION_S25FL004A 0x0212
+#define SPANSION_S25FL008A 0x0213
+#define SPANSION_S25FL016A 0x0214
+#define SPANSION_S25FL032A 0x0215 /* Same as S25FL032P, but the latter supports EDI and CFI */
+#define SPANSION_S25FL064A 0x0216 /* Same as S25FL064P, but the latter supports EDI and CFI */
+#define SPANSION_S25FL128 0x2018 /* Same ID for various S25FL127S, S25FL128P, S25FL128S and S25FL129P (including dual-die S70FL256P) variants (EDI supported) */
+#define SPANSION_S25FL256 0x0219
+#define SPANSION_S25FL512 0x0220
+#define SPANSION_S25FL204 0x4013
+#define SPANSION_S25FL208 0x4014
+#define SPANSION_S25FL216 0x4015 /* Same as S25FL216K, but the latter supports OTP, 3 status regs, quad I/O, SFDP etc. */
+#define SPANSION_S25FL132K 0x4016
+#define SPANSION_S25FL164K 0x4017
+
+/* Spansion 29GL families got a suffix indicating the process technology but share the same 3-Byte IDs. They can
+ * however be differentiated by CFI byte 45h. Some versions exist which have special top or bottom boot sectors
+ * and various WP configurations (not heeded in the table below).
+ *
+ * Suf. Process Sector Sz Rd Page Wr Page Data Width OTP Sz Min Size Max Size
+ * A 200 nm 64 kB 8 B 32 B x8/x16 256 B 16Mb/ 2MB 64Mb/ 8MB
+ * M 230 nm 64 kB 8 B 32 B x8/x16 256 B 32Mb/ 4MB 256Mb/ 32MB
+ * N* 110 nm 64 kB 16 B 32 B x8/x16 256 B 32Mb/ 4MB 64Mb/ 8MB
+ * N* 110 nm 128 kB 16 B 32 B x8/x16 256 B 128Mb/16MB 256Mb/ 64MB
+ * P 90 nm 128 kB 16 B 64 B x8/x16 256 B 128Mb/16MB 2Gb/256MB
+ * S 65 nm 128 kB 32 B 512 B x8 only 512 B 128Mb/16MB 2Gb/256MB
+ *
+ * For the N series there are two subgroups: the 4 and 8MB devices (S29GL032N, S29GL064N) have 64 kB erase
+ * sectors while the bigger chips got 128 kB sectors.
+ * Each series includes multiple models varying in speedgrade, boot block configurations etc.
+ */
+#define SPANSION_S29GL016_1 0xC4 /* Top Boot Sector, WP protects Top 2 sectors */
+#define SPANSION_S29GL016_2 0x49 /* Bottom Boot Sector, WP protects Bottom 2 sectors */
+/* Same IDs for S29GL032A, S29GL032M, S29GL032N (variations) */
+#define SPANSION_S29GL032_1289 0x7E1D00 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define SPANSION_S29GL032_3 0x7E1A01 /* Top Boot Sector, WP protects Top 2 sectors */
+#define SPANSION_S29GL032_4 0x7E1A00 /* Bottom Boot Sector, WP protects Bottom 2 sectors */
+/* Same IDs for S29GL064A, S29GL064M, S29GL064N, S29GL064S (variations) */
+#define SPANSION_S29GL064_1289 0x7E0C01 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define SPANSION_S29GL064_3 0x7E1001 /* Top Boot Sector, WP protects Top 2 sectors */
+#define SPANSION_S29GL064_4 0x7E1000 /* Bottom Boot Sector, WP protects Bottom 2 sectors */
+#define SPANSION_S29GL064_567 0x7E1301 /* x16 only, Uniform Sectors */
+
+#define SPANSION_S29GL128 0x7E2101 /* Same ID for S29GL128M, S29GL128N, S29GL128P, S29GL128S */
+#define SPANSION_S29GL256 0x7E2201 /* Same ID for S29GL256M, S29GL256N, S29GL256P, S29GL256S */
+#define SPANSION_S29GL512 0x7E2301 /* Same ID for S29GL512P, S29GL512S */
+#define SPANSION_S29GL01G 0x7E2801 /* Same ID for S29GL01GP, S29GL01GS */
+#define SPANSION_S70GL02G 0x7E4801 /* Same ID for S70GL02GP, S70GL02GS; based on two S29GL01G dies respectively */
+
+/*
+ * SST25 chips are SPI, first byte of device ID is memory type, second
+ * byte of device ID is related to log(bitsize) at least for some chips.
+ */
+#define SST_ID 0xBF /* SST */
+#define SST_SST25LF020_REMS 0x43 /* REMS or RES opcode */
+#define SST_SST25WF512 0x2501
+#define SST_SST25WF010 0x2502
+#define SST_SST25WF020 0x2503
+#define SST_SST25WF040 0x2504
+#define SST_SST25WF080 0x2505
+/* There exist some successors to members of the SST25WF family with alphabetic suffixes. Their datasheets show
+ * a 4 byte long response w/o a vendor ID. The first byte is 0x62 that is actually Sanyo's and might be due to
+ * a collaboration in the mid 2000ies between Sanyo and SST. */
+#define SST_SST25WF020A 0x1612
+#define SST_SST25WF040B 0x1613
+#define SST_SST25WF080B 0x1614
+#define SST_SST25VF512_REMS 0x48 /* REMS or RES opcode, same as SST25VF512A */
+#define SST_SST25VF010_REMS 0x49 /* REMS or RES opcode, same as SST25VF010A */
+#define SST_SST25VF020_REMS 0x43 /* REMS or RES opcode, same as SST25LF020A */
+#define SST_SST25VF020B 0x258C
+#define SST_SST25VF040_REMS 0x44 /* REMS or RES opcode, same as SST25LF040A */
+#define SST_SST25VF040B 0x258D
+#define SST_SST25VF040B_REMS 0x8D /* REMS or RES opcode */
+#define SST_SST25VF080_REMS 0x80 /* REMS or RES opcode, same as SST25LF080A */
+#define SST_SST25VF080B 0x258E
+#define SST_SST25VF080B_REMS 0x8E /* REMS or RES opcode */
+#define SST_SST25VF016B 0x2541
+#define SST_SST25VF032B 0x254A
+#define SST_SST25VF032B_REMS 0x4A /* REMS or RES opcode */
+#define SST_SST25VF064C 0x254B
+#define SST_SST26VF016 0x2601
+#define SST_SST26VF032 0x2602
+#define SST_SST26VF064B 0x2643
+#define SST_SST27SF512 0xA4
+#define SST_SST27SF010 0xA5
+#define SST_SST27SF020 0xA6
+#define SST_SST27VF010 0xA9
+#define SST_SST27VF020 0xAA
+#define SST_SST28SF040 0x04
+#define SST_SST29LE512 0x3D /* Same as SST29VE512 */
+#define SST_SST29EE512 0x5D
+#define SST_SST29EE010 0x07
+#define SST_SST29LE010 0x08 /* Same as SST29VE010 */
+#define SST_SST29EE020A 0x10 /* Same as SST29EE020 */
+#define SST_SST29LE020 0x12 /* Same as SST29VE020 */
+#define SST_SST29SF020 0x24
+#define SST_SST29VF020 0x25
+#define SST_SST29SF040 0x13
+#define SST_SST29VF040 0x14
+#define SST_SST39SF512 0xB4
+#define SST_SST39SF010 0xB5
+#define SST_SST39SF020 0xB6 /* Same as 39SF020A */
+#define SST_SST39SF040 0xB7
+#define SST_SST39VF512 0xD4
+#define SST_SST39VF010 0xD5
+#define SST_SST39VF020 0xD6 /* Same as 39LF020 */
+#define SST_SST39VF040 0xD7 /* Same as 39LF040 */
+#define SST_SST39VF080 0xD8 /* Same as 39LF080/39VF080/39VF088 */
+#define SST_SST45VF512 0x41 /* REMS, read opcode 0xFF */
+#define SST_SST45LF010 0x42 /* REMS, read opcode 0xFF, 'funny' other opcodes */
+#define SST_SST45VF010 0x45 /* REMS, read opcode 0xFF */
+#define SST_SST45VF020 0x43 /* REMS, read opcode 0xFF */
+#define SST_SST49LF040B 0x50
+#define SST_SST49LF040 0x51
+#define SST_SST49LF020 0x61
+#define SST_SST49LF020A 0x52
+#define SST_SST49LF030A 0x1C
+#define SST_SST49LF080A 0x5B
+#define SST_SST49LF002A 0x57
+#define SST_SST49LF003A 0x1B
+#define SST_SST49LF004A 0x60 /* Same as 49LF004B */
+#define SST_SST49LF008A 0x5A
+#define SST_SST49LF004C 0x54
+#define SST_SST49LF008C 0x59
+#define SST_SST49LF016C 0x5C
+#define SST_SST49LF160C 0x4C
+
+/*
+ * ST25P chips are SPI, first byte of device ID is memory type, second
+ * byte of device ID is related to log(bitsize) at least for some chips.
+ */
+#define ST_ID 0x20 /* ST / SGS/Thomson / Numonyx (later acquired by Micron) */
+#define ST_M25P05A 0x2010
+#define ST_M25P05_RES 0x10 /* Same code as M25P10. */
+#define ST_M25P10A 0x2011
+#define ST_M25P10_RES 0x10 /* Same code as M25P05. */
+#define ST_M25P20 0x2012
+#define ST_M25P20_RES 0x11
+#define ST_M25P40 0x2013
+#define ST_M25P40_RES 0x12
+#define ST_M25P80 0x2014
+#define ST_M25P16 0x2015
+#define ST_M25P32 0x2016
+#define ST_M25P64 0x2017
+#define ST_M25P128 0x2018
+#define ST_M45PE10 0x4011
+#define ST_M45PE20 0x4012
+#define ST_M45PE40 0x4013
+#define ST_M45PE80 0x4014
+#define ST_M45PE16 0x4015
+#define ST_M25PX80 0x7114
+#define ST_M25PX16 0x7115
+#define ST_M25PX32 0x7116
+#define ST_M25PX64 0x7117
+#define ST_M25PE10 0x8011
+#define ST_M25PE20 0x8012
+#define ST_M25PE40 0x8013
+#define ST_M25PE80 0x8014
+#define ST_M25PE16 0x8015
+#define ST_M50FLW040A 0x08
+#define ST_M50FLW040B 0x28
+#define ST_M50FLW080A 0x80
+#define ST_M50FLW080B 0x81
+#define ST_M50FW002 0x29
+#define ST_M50FW040 0x2C
+#define ST_M50FW080 0x2D
+#define ST_M50FW016 0x2E
+#define ST_M50LPW080 0x2F
+#define ST_M50LPW116 0x30
+#define ST_M29F002B 0x34 /* Same as M29F002BB */
+#define ST_M29F002T 0xB0 /* Same as M29F002BT/M29F002NT/M29F002BNT */
+#define ST_M29F040B 0xE2 /* Same as M29F040 */
+#define ST_M29F080 0xF1
+#define ST_M29F200BT 0xD3
+#define ST_M29F200BB 0xD4
+#define ST_M29F400BT 0xD5 /* Same as M29F400T */
+#define ST_M29F400BB 0xD6 /* Same as M29F400B */
+#define ST_M29F800DB 0x58
+#define ST_M29F800DT 0xEC
+#define ST_M29W010B 0x23
+#define ST_M29W040B 0xE3
+#define ST_M29W512B 0x27
+#define ST_M28W160ECB 0x88CF
+#define ST_M28W160ECT 0x88CE
+#define ST_M28W320FCB 0x88BB
+#define ST_M28W320FCT 0x88BA
+#define ST_M28W640HCB 0x8849
+#define ST_M28W640HCT 0x8848
+#define ST_M29DW127G 0x7E2004
+#define ST_M29W128GH 0x7E2101
+#define ST_M29W128GL 0x7E2100
+#define ST_M29W160EB 0x2249
+#define ST_M29W160ET 0x22C4
+#define ST_M29W256GH 0x7E21xx
+#define ST_M29W256GL 0x7E21xx
+#define ST_M29W320DB 0x88CB
+#define ST_M29W320DT 0x88CA
+#define ST_M29W400FB 0x00EF
+#define ST_M29W400FT 0x00EE
+#define ST_M29W512GH 0x7E2301
+#define ST_M29W640FB 0x22FD
+#define ST_M29W640FT 0x22ED
+#define ST_M29W640GB 0x7E1000
+#define ST_M29W640GH 0x7E0C01
+#define ST_M29W640GL 0x7E0C00
+#define ST_M29W640GT 0x7E1001
+#define ST_M29W800FB 0x225B
+#define ST_M29W800FT 0x22D7
+#define ST_M58BW16FB 0x8839
+#define ST_M58BW16FT 0x883A
+#define ST_M58BW32FB 0x8837
+#define ST_M58BW32FT 0x8838
+#define ST_M58WR016KB 0x8813
+#define ST_M58WR016KT 0x8812
+#define ST_M58WR032KB 0x8815
+#define ST_M58WR032KT 0x8814
+#define ST_M58WR064KB 0x8811
+#define ST_M58WR064KT 0x8810
+#define ST_MT28GU01G___1 0x88B0
+#define ST_MT28GU01G___2 0x88B1
+#define ST_MT28GU256___1 0x8901
+#define ST_MT28GU256___2 0x8904
+#define ST_MT28GU512___1 0x887E
+#define ST_MT28GU512___2 0x8881
+#define ST_N25Q016__1E 0xBB15 /* N25Q016, 1.8V, (uniform sectors expected) */
+#define ST_N25Q032__3E 0xBA16 /* N25Q032, 3.0V, (uniform sectors expected) */
+#define ST_N25Q032__1E 0xBB16 /* N25Q032, 1.8V, (uniform sectors expected) */
+#define ST_N25Q064__3E 0xBA17 /* N25Q064, 3.0V, (uniform sectors expected) */
+#define ST_N25Q064__1E 0xBB17 /* N25Q064, 1.8V, (uniform sectors expected) */
+#define ST_N25Q128__3E 0xBA18 /* N25Q128, 3.0V, (uniform sectors expected) */
+#define ST_N25Q128__1E 0xBB18 /* N25Q128, 1.8V, (uniform sectors expected) */
+#define ST_N25Q256__3E 0xBA19 /* N25Q256, 3.0V, (uniform sectors expected) */
+#define ST_N25Q256__1E 0xBB19 /* N25Q256, 1.8V, (uniform sectors expected) */
+#define ST_N25Q512__3E 0xBA20 /* N25Q512, 3.0V, (uniform sectors expected) */
+#define ST_N25Q512__1E 0xBB20 /* N25Q512, 1.8V, (uniform sectors expected) */
+#define ST_N25Q00A__3E 0xBA21 /* N25Q00A, 3.0V, (uniform sectors expected) */
+#define ST_NP5Q032 0xDA16 /* Phase-change memory (PCM), 3V */
+#define ST_NP5Q064 0xDA17 /* Phase-change memory (PCM), 3V */
+#define ST_NP5Q128 0xDA18 /* Phase-change memory (PCM), 3V */
+
+#define SYNCMOS_MVC_ID 0x40 /* SyncMOS (SM) and Mosel Vitelic Corporation (MVC) */
+#define MVC_V29C51000T 0x00
+#define MVC_V29C51400T 0x13
+#define MVC_V29LC51000 0x20
+#define MVC_V29LC51001 0x60
+#define MVC_V29LC51002 0x82
+#define MVC_V29C51000B 0xA0
+#define MVC_V29C51400B 0xB3
+#define SM_MVC_29C51001T 0x01 /* Identical chips: {F,S,V}29C51001T */
+#define SM_MVC_29C51002T 0x02 /* Identical chips: {F,S,V}29C51002T */
+#define SM_MVC_29C51004T 0x03 /* Identical chips: {F,S,V}29C51004T */
+#define SM_MVC_29C31004T 0x63 /* Identical chips: {S,V}29C31004T */
+#define SM_MVC_29C31004B 0x73 /* Identical chips: {S,V}29C31004B */
+#define SM_MVC_29C51001B 0xA1 /* Identical chips: {F,S,V}29C51001B */
+#define SM_MVC_29C51002B 0xA2 /* Identical chips: {F,S,V}29C51002B */
+#define SM_MVC_29C51004B 0xA3 /* Identical chips: {F,S,V}29C51004B */
+
+#define TENX_ID 0x7F7F5E /* Tenx Technologies */
+#define TENX_ID_NOPREFIX 0x5E
+#define TENX_ICE25P05 0x01 /* Maybe? */
+
+#define TI_ID 0x97 /* Texas Instruments */
+#define TI_OLD_ID 0x01 /* TI chips from last century */
+#define TI_TMS29F002RT 0xB0
+#define TI_TMS29F002RB 0x34
+
+/*
+ * W25X chips are SPI, first byte of device ID is memory type, second
+ * byte of device ID is related to log(bitsize).
+ */
+#define WINBOND_NEX_ID 0xEF /* Winbond (ex Nexcom) serial flashes */
+#define WINBOND_NEX_W25X10 0x3011
+#define WINBOND_NEX_W25X20 0x3012
+#define WINBOND_NEX_W25X40 0x3013
+#define WINBOND_NEX_W25X80 0x3014
+#define WINBOND_NEX_W25X16 0x3015
+#define WINBOND_NEX_W25X32 0x3016
+#define WINBOND_NEX_W25X64 0x3017
+#define WINBOND_NEX_W25Q40_V 0x4013 /* W25Q40BV; W25Q40BL (2.3-3.6V) */
+#define WINBOND_NEX_W25Q80_V 0x4014 /* W25Q80BV */
+#define WINBOND_NEX_W25Q16_V 0x4015 /* W25Q16CV; W25Q16DV */
+#define WINBOND_NEX_W25Q32_V 0x4016 /* W25Q32BV; W25Q32FV in SPI mode (default) */
+#define WINBOND_NEX_W25Q64_V 0x4017 /* W25Q64BV, W25Q64CV; W25Q64FV in SPI mode (default) */
+#define WINBOND_NEX_W25Q128_V 0x4018 /* W25Q128BV; W25Q128FV in SPI mode (default) */
+#define WINBOND_NEX_W25Q256_V 0x4019 /* W25Q256FV */
+#define WINBOND_NEX_W25Q20_W 0x5012 /* W25Q20BW */
+#define WINBOND_NEX_W25Q40_W 0x5013 /* W25Q40BW */
+#define WINBOND_NEX_W25Q80_W 0x5014 /* W25Q80BW */
+#define WINBOND_NEX_W25Q16_W 0x6015 /* W25Q16DW */
+#define WINBOND_NEX_W25Q32_W 0x6016 /* W25Q32DW; W25Q32FV in QPI mode */
+#define WINBOND_NEX_W25Q64_W 0x6017 /* W25Q64DW; W25Q64FV in QPI mode */
+#define WINBOND_NEX_W25Q128_W 0x6018 /* (No W version known) W25Q128FV in QPI mode */
+
+#define WINBOND_ID 0xDA /* Winbond */
+#define WINBOND_W19B160BB 0x49
+#define WINBOND_W19B160BT 0xC4
+#define WINBOND_W19B320SB 0x2A /* Same as W19L320SB */
+#define WINBOND_W19B320ST 0xBA /* Same as W19L320ST */
+#define WINBOND_W19B322MB 0x92
+#define WINBOND_W19B322MT 0x10
+#define WINBOND_W19B323MB 0x94
+#define WINBOND_W19B323MT 0x13
+#define WINBOND_W19B324MB 0x97
+#define WINBOND_W19B324MT 0x16
+#define WINBOND_W29C010 0xC1 /* Same as W29C010M, W29C011A, W29EE011, W29EE012, and ASD AE29F1008 */
+#define WINBOND_W29C020 0x45 /* Same as W29C020C, W29C022 and ASD AE29F2008 */
+#define WINBOND_W29C040 0x46 /* Same as W29C040P */
+#define WINBOND_W29C512A 0xC8 /* Same as W29EE512 */
+#define WINBOND_W29GL032CHL 0x7E1D01 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define WINBOND_W29GL032CB 0x7E1A00 /* Top Boot Sector, WP protects Top 2 sectors */
+#define WINBOND_W29GL032CT 0x7E1A01 /* Bottom Boot Sector, WP protects Bottom 2 sectors */
+#define WINBOND_W29GL064CHL 0x7E0C01 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define WINBOND_W29GL064CB 0x7E1000 /* Top Boot Sector, WP protects Top 2 sectors */
+#define WINBOND_W29GL064CT 0x7E1001 /* Bottom Boot Sector, WP protects Bottom 2 sectors */
+#define WINBOND_W29GL128CHL 0x7E2101 /* Uniform Sectors, WP protects Top OR Bottom sector */
+#define WINBOND_W29GL256HL 0x7E2201 /* Same ID for W29GL0256P and W29GL0256S; uniform Sectors, WP protects Top OR Bottom sector */
+#define WINBOND_W39F010 0xA1
+#define WINBOND_W39L010 0x31
+#define WINBOND_W39L020 0xB5
+#define WINBOND_W39L040 0xB6
+#define WINBOND_W39L040A 0xD6
+#define WINBOND_W39L512 0x38
+#define WINBOND_W39V040A 0x3D
+#define WINBOND_W39V040FA 0x34
+#define WINBOND_W39V040B 0x54 /* Same as W39V040FB */
+#define WINBOND_W39V040C 0x50 /* Same as W39V040FC */
+#define WINBOND_W39V080A 0xD0
+#define WINBOND_W39V080FA 0xD3
+#define WINBOND_W39V080FA_DM 0x93 /* W39V080FA dual mode */
+#define WINBOND_W49F002 0x25 /* Same as W49F002B */
+#define WINBOND_W49F002U 0x0B /* Same as W49F002N and ASD AE49F2008 */
+#define WINBOND_W49F020 0x8C
+#define WINBOND_W49V002A 0xB0
+#define WINBOND_W49V002FA 0x32
+
+#endif /* !FLASHCHIPS_H */
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
new file mode 100644
index 0000000..34e1fe7
--- /dev/null
+++ b/flashrom.8.tmpl
@@ -0,0 +1,1251 @@
+.\" Load the www device when using groff; provide a fallback for groff's MTO macro that formats email addresses.
+.ie \n[.g] \
+. mso www.tmac
+.el \{
+. de MTO
+ \\$2 \(la\\$1 \(ra\\$3 \
+. .
+.\}
+.\" Create wrappers for .MTO and .URL that print only text on systems w/o groff or if not outputting to a HTML
+.\" device. To that end we need to distinguish HTML output on groff from other configurations first.
+.nr groffhtml 0
+.if \n[.g] \
+. if "\*[.T]"html" \
+. nr groffhtml 1
+.\" For code reuse it would be nice to have a single wrapper that gets its target macro as parameter.
+.\" However, this did not work out with NetBSD's and OpenBSD's groff...
+.de URLB
+. ie (\n[groffhtml]==1) \{\
+. URL \\$@
+. \}
+. el \{\
+. ie "\\$2"" \{\
+. BR "\\$1" "\\$3"
+. \}
+. el \{\
+. RB "\\$2 \(la" "\\$1" "\(ra\\$3"
+. \}
+. \}
+..
+.de MTOB
+. ie (\n[groffhtml]==1) \{\
+. MTO \\$@
+. \}
+. el \{\
+. ie "\\$2"" \{\
+. BR "\\$1" "\\$3"
+. \}
+. el \{\
+. RB "\\$2 \(la" "\\$1" "\(ra\\$3"
+. \}
+. \}
+..
+.TH FLASHROM 8 "" ""
+.SH NAME
+flashrom \- detect, read, write, verify and erase flash chips
+.SH SYNOPSIS
+.B flashrom \fR[\fB\-h\fR|\fB\-R\fR|\fB\-L\fR|\fB\-z\fR|\
+\fB\-p\fR <programmername>[:<parameters>]
+ [\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\fB\-v\fR <file>] \
+[\fB\-c\fR <chipname>]
+ [\fB\-l\fR <file> [\fB\-i\fR <image>]] [\fB\-n\fR] [\fB\-f\fR]]
+ [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>]
+.SH DESCRIPTION
+.B flashrom
+is a utility for detecting, reading, writing, verifying and erasing flash
+chips. It's often used to flash BIOS/EFI/coreboot/firmware images in-system
+using a supported mainboard. However, it also supports various external
+PCI/USB/parallel-port/serial-port based devices which can program flash chips,
+including some network cards (NICs), SATA/IDE controller cards, graphics cards,
+the Bus Pirate device, various FTDI FT2232/FT4232H/FT232H based USB devices, and more.
+.PP
+It supports a wide range of DIP32, PLCC32, DIP8, SO8/SOIC8, TSOP32, TSOP40,
+TSOP48, and BGA chips, which use various protocols such as LPC, FWH,
+parallel flash, or SPI.
+.SH OPTIONS
+.B IMPORTANT:
+Please note that the command line interface for flashrom will change before
+flashrom 1.0. Do not use flashrom in scripts or other automated tools without
+checking that your flashrom version won't interpret options in a different way.
+.PP
+You can specify one of
+.BR \-h ", " \-R ", " \-L ", " \-z ", " \-E ", " \-r ", " \-w ", " \-v
+or no operation.
+If no operation is specified, flashrom will only probe for flash chips. It is
+recommended that if you try flashrom the first time on a system, you run it
+in probe-only mode and check the output. Also you are advised to make a
+backup of your current ROM contents with
+.B \-r
+before you try to write a new image. All operations involving any chip access (probe/read/write/...) require the
+.B -p/--programmer
+option to be used (please see below).
+.TP
+.B "\-r, \-\-read <file>"
+Read flash ROM contents and save them into the given
+.BR <file> .
+If the file already exists, it will be overwritten.
+.TP
+.B "\-w, \-\-write <file>"
+Write
+.B <file>
+into flash ROM. This will first automatically
+.B erase
+the chip, then write to it.
+.sp
+In the process the chip is also read several times. First an in-memory backup
+is made for disaster recovery and to be able to skip regions that are
+already equal to the image file. This copy is updated along with the write
+operation. In case of erase errors it is even re-read completely. After
+writing has finished and if verification is enabled, the whole flash chip is
+read out and compared with the input image.
+.TP
+.B "\-n, \-\-noverify"
+Skip the automatic verification of flash ROM contents after writing. Using this
+option is
+.B not
+recommended, you should only use it if you know what you are doing and if you
+feel that the time for verification takes too long.
+.sp
+Typical usage is:
+.B "flashrom \-p prog \-n \-w <file>"
+.sp
+This option is only useful in combination with
+.BR \-\-write .
+.TP
+.B "\-v, \-\-verify <file>"
+Verify the flash ROM contents against the given
+.BR <file> .
+.TP
+.B "\-E, \-\-erase"
+Erase the flash ROM chip.
+.TP
+.B "\-V, \-\-verbose"
+More verbose output. This option can be supplied multiple times
+(max. 3 times, i.e.
+.BR \-VVV )
+for even more debug output.
+.TP
+.B "\-c, \-\-chip" <chipname>
+Probe only for the specified flash ROM chip. This option takes the chip name as
+printed by
+.B "flashrom \-L"
+without the vendor name as parameter. Please note that the chip name is
+case sensitive.
+.TP
+.B "\-f, \-\-force"
+Force one or more of the following actions:
+.sp
+* Force chip read and pretend the chip is there.
+.sp
+* Force chip access even if the chip is bigger than the maximum supported \
+size for the flash bus.
+.sp
+* Force erase even if erase is known bad.
+.sp
+* Force write even if write is known bad.
+.TP
+.B "\-l, \-\-layout <file>"
+Read ROM layout from
+.BR <file> .
+.sp
+flashrom supports ROM layouts. This allows you to flash certain parts of
+the flash chip only. A ROM layout file contains multiple lines with the
+following syntax:
+.sp
+.B " startaddr:endaddr imagename"
+.sp
+.BR "startaddr " "and " "endaddr "
+are hexadecimal addresses within the ROM file and do not refer to any
+physical address. Please note that using a 0x prefix for those hexadecimal
+numbers is not necessary, but you can't specify decimal/octal numbers.
+.BR "imagename " "is an arbitrary name for the region/image from"
+.BR " startaddr " "to " "endaddr " "(both addresses included)."
+.sp
+Example:
+.sp
+ 00000000:00008fff gfxrom
+ 00009000:0003ffff normal
+ 00040000:0007ffff fallback
+.sp
+If you only want to update the image named
+.BR "normal " "in a ROM based on the layout above, run"
+.sp
+.B " flashrom \-p prog \-\-layout rom.layout \-\-image normal \-w some.rom"
+.sp
+To update only the images named
+.BR "normal " "and " "fallback" ", run:"
+.sp
+.B " flashrom \-p prog \-l rom.layout \-i normal -i fallback \-w some.rom"
+.sp
+Overlapping sections are not supported.
+.TP
+.B "\-i, \-\-image <imagename>"
+Only flash region/image
+.B <imagename>
+from flash layout.
+.TP
+.B "\-L, \-\-list\-supported"
+List the flash chips, chipsets, mainboards, and external programmers
+(including PCI, USB, parallel port, and serial port based devices)
+supported by flashrom.
+.sp
+There are many unlisted boards which will work out of the box, without
+special support in flashrom. Please let us know if you can verify that
+other boards work or do not work out of the box.
+.sp
+.B IMPORTANT:
+For verification you have
+to test an ERASE and/or WRITE operation, so make sure you only do that
+if you have proper means to recover from failure!
+.TP
+.B "\-z, \-\-list\-supported-wiki"
+Same as
+.BR \-\-list\-supported ,
+but outputs the supported hardware in MediaWiki syntax, so that it can be
+easily pasted into the
+.URLB https://flashrom.org/Supported_hardware "supported hardware wiki page" .
+Please note that MediaWiki output is not compiled in by default.
+.TP
+.B "\-p, \-\-programmer <name>[:parameter[,parameter[,parameter]]]"
+Specify the programmer device. This is mandatory for all operations
+involving any chip access (probe/read/write/...). Currently supported are:
+.sp
+.BR "* internal" " (for in-system flashing in the mainboard)"
+.sp
+.BR "* dummy" " (virtual programmer for testing flashrom)"
+.sp
+.BR "* nic3com" " (for flash ROMs on 3COM network cards)"
+.sp
+.BR "* nicrealtek" " (for flash ROMs on Realtek and SMC 1211 network cards)"
+.sp
+.BR "* nicnatsemi" " (for flash ROMs on National Semiconductor DP838* network \
+cards)"
+.sp
+.BR "* nicintel" " (for parallel flash ROMs on Intel 10/100Mbit network cards)
+.sp
+.BR "* gfxnvidia" " (for flash ROMs on NVIDIA graphics cards)"
+.sp
+.BR "* drkaiser" " (for flash ROMs on Dr. Kaiser PC-Waechter PCI cards)"
+.sp
+.BR "* satasii" " (for flash ROMs on Silicon Image SATA/IDE controllers)"
+.sp
+.BR "* satamv" " (for flash ROMs on Marvell SATA controllers)"
+.sp
+.BR "* atahpt" " (for flash ROMs on Highpoint ATA/RAID controllers)"
+.sp
+.BR "* atavia" " (for flash ROMs on VIA VT6421A SATA controllers)"
+.sp
+.BR "* atapromise" " (for flash ROMs on Promise PDC2026x ATA/RAID controllers)"
+.sp
+.BR "* it8212" " (for flash ROMs on ITE IT8212F ATA/RAID controller)"
+.sp
+.BR "* ft2232_spi" " (for SPI flash ROMs attached to an FT2232/FT4232H/FT232H family based USB SPI programmer).
+.sp
+.BR "* serprog" " (for flash ROMs attached to a programmer speaking serprog, \
+including some Arduino-based devices)."
+.sp
+.BR "* buspirate_spi" " (for SPI flash ROMs attached to a Bus Pirate)"
+.sp
+.BR "* dediprog" " (for SPI flash ROMs attached to a Dediprog SF100)"
+.sp
+.BR "* rayer_spi" " (for SPI flash ROMs attached to a parallel port by one of various cable types)"
+.sp
+.BR "* pony_spi" " (for SPI flash ROMs attached to a SI-Prog serial port "
+bitbanging adapter)
+.sp
+.BR "* nicintel_spi" " (for SPI flash ROMs on Intel Gigabit network cards)"
+.sp
+.BR "* ogp_spi" " (for SPI flash ROMs on Open Graphics Project graphics card)"
+.sp
+.BR "* linux_spi" " (for SPI flash ROMs accessible via /dev/spidevX.Y on Linux)"
+.sp
+.BR "* usbblaster_spi" " (for SPI flash ROMs attached to an Altera USB-Blaster compatible cable)"
+.sp
+.BR "* nicintel_eeprom" " (for SPI EEPROMs on Intel Gigabit network cards)"
+.sp
+.BR "* mstarddc_spi" " (for SPI flash ROMs accessible through DDC in MSTAR-equipped displays)"
+.sp
+.BR "* pickit2_spi" " (for SPI flash ROMs accessible via Microchip PICkit2)"
+.sp
+.BR "* ch341a_spi" " (for SPI flash ROMs attached to WCH CH341A)"
+.sp
+Some programmers have optional or mandatory parameters which are described
+in detail in the
+.B PROGRAMMER-SPECIFIC INFORMATION
+section. Support for some programmers can be disabled at compile time.
+.B "flashrom \-h"
+lists all supported programmers.
+.TP
+.B "\-h, \-\-help"
+Show a help text and exit.
+.TP
+.B "\-o, \-\-output <logfile>"
+Save the full debug log to
+.BR <logfile> .
+If the file already exists, it will be overwritten. This is the recommended
+way to gather logs from flashrom because they will be verbose even if the
+on-screen messages are not verbose and don't require output redirection.
+.TP
+.B "\-R, \-\-version"
+Show version information and exit.
+.SH PROGRAMMER-SPECIFIC INFORMATION
+Some programmer drivers accept further parameters to set programmer-specific
+parameters. These parameters are separated from the programmer name by a
+colon. While some programmers take arguments at fixed positions, other
+programmers use a key/value interface in which the key and value is separated
+by an equal sign and different pairs are separated by a comma or a colon.
+.SS
+.BR "internal " programmer
+.TP
+.B Board Enables
+.sp
+Some mainboards require to run mainboard specific code to enable flash erase
+and write support (and probe support on old systems with parallel flash).
+The mainboard brand and model (if it requires specific code) is usually
+autodetected using one of the following mechanisms: If your system is
+running coreboot, the mainboard type is determined from the coreboot table.
+Otherwise, the mainboard is detected by examining the onboard PCI devices
+and possibly DMI info. If PCI and DMI do not contain information to uniquely
+identify the mainboard (which is the exception), or if you want to override
+the detected mainboard model, you can specify the mainboard using the
+.sp
+.B " flashrom \-p internal:mainboard=<vendor>:<board>"
+syntax.
+.sp
+See the 'Known boards' or 'Known laptops' section in the output
+of 'flashrom \-L' for a list of boards which require the specification of
+the board name, if no coreboot table is found.
+.sp
+Some of these board-specific flash enabling functions (called
+.BR "board enables" )
+in flashrom have not yet been tested. If your mainboard is detected needing
+an untested board enable function, a warning message is printed and the
+board enable is not executed, because a wrong board enable function might
+cause the system to behave erratically, as board enable functions touch the
+low-level internals of a mainboard. Not executing a board enable function
+(if one is needed) might cause detection or erasing failure. If your board
+protects only part of the flash (commonly the top end, called boot block),
+flashrom might encounter an error only after erasing the unprotected part,
+so running without the board-enable function might be dangerous for erase
+and write (which includes erase).
+.sp
+The suggested procedure for a mainboard with untested board specific code is
+to first try to probe the ROM (just invoke flashrom and check that it
+detects your flash chip type) without running the board enable code (i.e.
+without any parameters). If it finds your chip, fine. Otherwise, retry
+probing your chip with the board-enable code running, using
+.sp
+.B " flashrom \-p internal:boardenable=force"
+.sp
+If your chip is still not detected, the board enable code seems to be broken
+or the flash chip unsupported. Otherwise, make a backup of your current ROM
+contents (using
+.BR \-r )
+and store it to a medium outside of your computer, like
+a USB drive or a network share. If you needed to run the board enable code
+already for probing, use it for reading too.
+If reading succeeds and the contens of the read file look legit you can try to write the new image.
+You should enable the board enable code in any case now, as it
+has been written because it is known that writing/erasing without the board
+enable is going to fail. In any case (success or failure), please report to
+the flashrom mailing list, see below.
+.sp
+.TP
+.B Coreboot
+.sp
+On systems running coreboot, flashrom checks whether the desired image matches
+your mainboard. This needs some special board ID to be present in the image.
+If flashrom detects that the image you want to write and the current board
+do not match, it will refuse to write the image unless you specify
+.sp
+.B " flashrom \-p internal:boardmismatch=force"
+.TP
+.B ITE IT87 Super I/O
+.sp
+If your mainboard is manufactured by GIGABYTE and supports DualBIOS it is very likely that it uses an
+ITE IT87 series Super I/O to switch between the two flash chips. Only one of them can be accessed at a time
+and you can manually select which one to use with the
+.sp
+.B " flashrom \-p internal:dualbiosindex=chip"
+.sp
+syntax where
+.B chip
+is the index of the chip to use (0 = main, 1 = backup). You can check which one is currently selected by
+leaving out the
+.B chip
+parameter.
+.sp
+If your mainboard uses an ITE IT87 series Super I/O for LPC<->SPI flash bus
+translation, flashrom should autodetect that configuration. If you want to
+set the I/O base port of the IT87 series SPI controller manually instead of
+using the value provided by the BIOS, use the
+.sp
+.B " flashrom \-p internal:it87spiport=portnum"
+.sp
+syntax where
+.B portnum
+is the I/O port number (must be a multiple of 8). In the unlikely case
+flashrom doesn't detect an active IT87 LPC<->SPI bridge, please send a bug
+report so we can diagnose the problem.
+.sp
+.TP
+.B AMD chipsets
+.sp
+Beginning with the SB700 chipset there is an integrated microcontroller (IMC) based on the 8051 embedded in
+every AMD southbridge. Its firmware resides in the same flash chip as the host's which makes writing to the
+flash risky if the IMC is active. Flashrom tries to temporarily disable the IMC but even then changing the
+contents of the flash can have unwanted effects: when the IMC continues (at the latest after a reboot) it will
+continue executing code from the flash. If the code was removed or changed in an unfortunate way it is
+unpredictable what the IMC will do. Therefore, if flashrom detects an active IMC it will disable write support
+unless the user forces it with the
+.sp
+.B " flashrom \-p internal:amd_imc_force=yes"
+.sp
+syntax. The user is responsible for supplying a suitable image or leaving out the IMC region with the help of
+a layout file. This limitation might be removed in the future when we understand the details better and have
+received enough feedback from users. Please report the outcome if you had to use this option to write a chip.
+.sp
+An optional
+.B spispeed
+parameter specifies the frequency of the SPI bus where applicable (i.e.\& SB600 or later with an SPI flash chip
+directly attached to the chipset).
+Syntax is
+.sp
+.B " flashrom \-p internal:spispeed=frequency"
+.sp
+where
+.B frequency
+can be
+.BR "'16.5\ MHz'" ", " "'22\ MHz'" ", " "'33\ MHz'" ", " "'66\ MHz'" ", " "'100\ MHZ'" ", or " "'800\ kHz'" "."
+Support of individual frequencies depends on the generation of the chipset:
+.sp
+* SB6xx, SB7xx, SP5xxx: from 16.5 MHz up to and including 33 MHz
+.sp
+* SB8xx, SB9xx, Hudson: from 16.5 MHz up to and including 66 MHz
+.sp
+* Yangtze (with SPI 100 engine as found in Kabini and Tamesh): all of them
+.sp
+The default is to use 16.5 MHz and disable Fast Reads.
+.TP
+.B Intel chipsets
+.sp
+If you have an Intel chipset with an ICH8 or later southbridge with SPI flash
+attached, and if a valid descriptor was written to it (e.g.\& by the vendor), the
+chipset provides an alternative way to access the flash chip(s) named
+.BR "Hardware Sequencing" .
+It is much simpler than the normal access method (called
+.BR "Software Sequencing" "),"
+but does not allow the software to choose the SPI commands to be sent.
+You can use the
+.sp
+.B " flashrom \-p internal:ich_spi_mode=value"
+.sp
+syntax where
+.BR "value " "can be"
+.BR auto ", " swseq " or " hwseq .
+By default
+.RB "(or when setting " ich_spi_mode=auto )
+the module tries to use swseq and only activates hwseq if need be (e.g.\& if
+important opcodes are inaccessible due to lockdown; or if more than one flash
+chip is attached). The other options (swseq, hwseq) select the respective mode
+(if possible).
+.sp
+ICH8 and later southbridges may also have locked address ranges of different
+kinds if a valid descriptor was written to it. The flash address space is then
+partitioned in multiple so called "Flash Regions" containing the host firmware,
+the ME firmware and so on respectively. The flash descriptor can also specify up
+to 5 so called "Protected Regions", which are freely chosen address ranges
+independent from the aforementioned "Flash Regions". All of them can be write
+and/or read protected individually. If flashrom detects such a lock it will
+disable write support unless the user forces it with the
+.sp
+.B " flashrom \-p internal:ich_spi_force=yes"
+.sp
+syntax. If this leads to erase or write accesses to the flash it would most
+probably bring it into an inconsistent and unbootable state and we will not
+provide any support in such a case.
+.sp
+If you have an Intel chipset with an ICH2 or later southbridge and if you want
+to set specific IDSEL values for a non-default flash chip or an embedded
+controller (EC), you can use the
+.sp
+.B " flashrom \-p internal:fwh_idsel=value"
+.sp
+syntax where
+.B value
+is the 48-bit hexadecimal raw value to be written in the
+IDSEL registers of the Intel southbridge. The upper 32 bits use one hex digit
+each per 512 kB range between 0xffc00000 and 0xffffffff, and the lower 16 bits
+use one hex digit each per 1024 kB range between 0xff400000 and 0xff7fffff.
+The rightmost hex digit corresponds with the lowest address range. All address
+ranges have a corresponding sister range 4 MB below with identical IDSEL
+settings. The default value for ICH7 is given in the example below.
+.sp
+Example:
+.B "flashrom \-p internal:fwh_idsel=0x001122334567"
+.TP
+.B Laptops
+.sp
+Using flashrom on laptops is dangerous and may easily make your hardware
+unusable (see also the
+.B BUGS
+section). The embedded controller (EC) in these
+machines often interacts badly with flashing.
+More information is
+.URLB https://flashrom.org/Laptops "in the wiki" .
+For example the EC firmware sometimes resides on the same
+flash chip as the host firmware. While flashrom tries to change the contents of
+that memory the EC might need to fetch new instructions or data from it and
+could stop working correctly. Probing for and reading from the chip may also
+irritate your EC and cause fan failure, backlight failure, sudden poweroff, and
+other nasty effects. flashrom will attempt to detect if it is running on a
+laptop and abort immediately for safety reasons if it clearly identifies the
+host computer as one. If you want to proceed anyway at your own risk, use
+.sp
+.B " flashrom \-p internal:laptop=force_I_want_a_brick"
+.sp
+We will not help you if you force flashing on a laptop because this is a really
+dumb idea.
+.sp
+You have been warned.
+.sp
+Currently we rely on the chassis type encoded in the DMI/SMBIOS data to detect
+laptops. Some vendors did not implement those bits correctly or set them to
+generic and/or dummy values. flashrom will then issue a warning and bail out
+like above. In this case you can use
+.sp
+.B " flashrom \-p internal:laptop=this_is_not_a_laptop"
+.sp
+to tell flashrom (at your own risk) that it is not running on a laptop.
+.SS
+.BR "dummy " programmer
+.IP
+The dummy programmer operates on a buffer in memory only. It provides a safe and fast way to test various
+aspects of flashrom and is mainly used in development and while debugging.
+It is able to emulate some chips to a certain degree (basic
+identify/read/erase/write operations work).
+.sp
+An optional parameter specifies the bus types it
+should support. For that you have to use the
+.sp
+.B " flashrom \-p dummy:bus=[type[+type[+type]]]"
+.sp
+syntax where
+.B type
+can be
+.BR parallel ", " lpc ", " fwh ", " spi
+in any order. If you specify bus without type, all buses will be disabled.
+If you do not specify bus, all buses will be enabled.
+.sp
+Example:
+.B "flashrom \-p dummy:bus=lpc+fwh"
+.sp
+The dummy programmer supports flash chip emulation for automated self-tests
+without hardware access. If you want to emulate a flash chip, use the
+.sp
+.B " flashrom \-p dummy:emulate=chip"
+.sp
+syntax where
+.B chip
+is one of the following chips (please specify only the chip name, not the
+vendor):
+.sp
+.RB "* ST " M25P10.RES " SPI flash chip (128 kB, RES, page write)"
+.sp
+.RB "* SST " SST25VF040.REMS " SPI flash chip (512 kB, REMS, byte write)"
+.sp
+.RB "* SST " SST25VF032B " SPI flash chip (4096 kB, RDID, AAI write)"
+.sp
+.RB "* Macronix " MX25L6436 " SPI flash chip (8192 kB, RDID, SFDP)"
+.sp
+Example:
+.B "flashrom -p dummy:emulate=SST25VF040.REMS"
+.TP
+.B Persistent images
+.sp
+If you use flash chip emulation, flash image persistence is available as well
+by using the
+.sp
+.B " flashrom \-p dummy:emulate=chip,image=image.rom"
+.sp
+syntax where
+.B image.rom
+is the file where the simulated chip contents are read on flashrom startup and
+where the chip contents on flashrom shutdown are written to.
+.sp
+Example:
+.B "flashrom -p dummy:emulate=M25P10.RES,image=dummy.bin"
+.TP
+.B SPI write chunk size
+.sp
+If you use SPI flash chip emulation for a chip which supports SPI page write
+with the default opcode, you can set the maximum allowed write chunk size with
+the
+.sp
+.B " flashrom \-p dummy:emulate=chip,spi_write_256_chunksize=size"
+.sp
+syntax where
+.B size
+is the number of bytes (min.\& 1, max.\& 256).
+.sp
+Example:
+.sp
+.B " flashrom -p dummy:emulate=M25P10.RES,spi_write_256_chunksize=5"
+.TP
+.B SPI blacklist
+.sp
+To simulate a programmer which refuses to send certain SPI commands to the
+flash chip, you can specify a blacklist of SPI commands with the
+.sp
+.B " flashrom -p dummy:spi_blacklist=commandlist"
+.sp
+syntax where
+.B commandlist
+is a list of two-digit hexadecimal representations of
+SPI commands. If commandlist is e.g.\& 0302, flashrom will behave as if the SPI
+controller refuses to run command 0x03 (READ) and command 0x02 (WRITE).
+commandlist may be up to 512 characters (256 commands) long.
+Implementation note: flashrom will detect an error during command execution.
+.sp
+.TP
+.B SPI ignorelist
+.sp
+To simulate a flash chip which ignores (doesn't support) certain SPI commands,
+you can specify an ignorelist of SPI commands with the
+.sp
+.B " flashrom -p dummy:spi_ignorelist=commandlist"
+.sp
+syntax where
+.B commandlist
+is a list of two-digit hexadecimal representations of
+SPI commands. If commandlist is e.g.\& 0302, the emulated flash chip will ignore
+command 0x03 (READ) and command 0x02 (WRITE). commandlist may be up to 512
+characters (256 commands) long.
+Implementation note: flashrom won't detect an error during command execution.
+.sp
+.TP
+.B SPI status register
+.sp
+You can specify the initial content of the chip's status register with the
+.sp
+.B " flashrom -p dummy:spi_status=content"
+.sp
+syntax where
+.B content
+is an 8-bit hexadecimal value.
+.SS
+.BR "nic3com" , " nicrealtek" , " nicnatsemi" , " nicintel", " nicintel_eeprom"\
+, " nicintel_spi" , " gfxnvidia" , " ogp_spi" , " drkaiser" , " satasii"\
+, " satamv" , " atahpt", " atavia ", " atapromise " and " it8212 " programmers
+.IP
+These programmers have an option to specify the PCI address of the card
+your want to use, which must be specified if more than one card supported
+by the selected programmer is installed in your system. The syntax is
+.sp
+.BR " flashrom \-p xxxx:pci=bb:dd.f" ,
+.sp
+where
+.B xxxx
+is the name of the programmer,
+.B bb
+is the PCI bus number,
+.B dd
+is the PCI device number, and
+.B f
+is the PCI function number of the desired device.
+.sp
+Example:
+.B "flashrom \-p nic3com:pci=05:04.0"
+.SS
+.BR "atavia " programmer
+.IP
+Due to the mysterious address handling of the VIA VT6421A controller the user can specify an offset with the
+.sp
+.B " flashrom \-p atavia:offset=addr"
+.sp
+syntax where
+.B addr
+will be interpreted as usual (leading 0x (0) for hexadecimal (octal) values, or else decimal).
+For more information please see
+.URLB https://flashrom.org/VT6421A "its wiki page" .
+.SS
+.BR "atapromise " programmer
+.IP
+This programmer is currently limited to 32 kB, regardless of the actual size of the flash chip. This stems
+from the fact that, on the tested device (a Promise Ultra100), not all of the chip's address lines were
+actually connected. You may use this programmer to flash firmware updates, since these are only 16 kB in
+size (padding to 32 kB is required).
+.SS
+.BR "nicintel_eeprom " programmer
+.IP
+This is the first programmer module in flashrom that does not provide access to NOR flash chips but EEPROMs
+mounted on gigabit Ethernet cards based on Intel's 82580 NIC. Because EEPROMs normally do not announce their
+size nor allow themselves to be identified, the controller relies on correct size values written to predefined
+addresses within the chip. Flashrom follows this scheme but assumes the minimum size of 16 kB (128 kb) if an
+unprogrammed EEPROM/card is detected. Intel specifies following EEPROMs to be compatible:
+Atmel AT25128, AT25256, Micron (ST) M95128, M95256 and OnSemi (Catalyst) CAT25CS128.
+.SS
+.BR "ft2232_spi " programmer
+.IP
+This module supports various programmers based on FTDI FT2232/FT4232H/FT232H chips including the DLP Design
+DLP-USB1232H, openbiosprog-spi, Amontec JTAGkey/JTAGkey-tiny/JTAGkey-2, Dangerous Prototypes Bus Blaster,
+Olimex ARM-USB-TINY/-H, Olimex ARM-USB-OCD/-H, OpenMoko Neo1973 Debug board (V2+), TIAO/DIYGADGET USB
+Multi-Protocol Adapter (TUMPA), TUMPA Lite, GOEPEL PicoTAP and Google Servo v1/v2.
+.sp
+An optional parameter specifies the controller
+type and channel/interface/port it should support. For that you have to use the
+.sp
+.B " flashrom \-p ft2232_spi:type=model,port=interface"
+.sp
+syntax where
+.B model
+can be
+.BR 2232H ", " 4232H ", " 232H ", " jtagkey ", " busblaster ", " openmoko ", " \
+arm-usb-tiny ", " arm-usb-tiny-h ", " arm-usb-ocd ", " arm-usb-ocd-h \
+", " tumpa ", " tumpalite ", " picotap ", " google-servo ", " google-servo-v2 \
+" or " google-servo-v2-legacy
+and
+.B interface
+can be
+.BR A ", " B ", " C ", or " D .
+The default model is
+.B 4232H
+and the default interface is
+.BR A .
+.sp
+If there is more than one ft2232_spi-compatible device connected, you can select which one should be used by
+specifying its serial number with the
+.sp
+.B " flashrom \-p ft2232_spi:serial=number"
+.sp
+syntax where
+.B number
+is the serial number of the device (which can be found for example in the output of lsusb -v).
+.sp
+All models supported by the ft2232_spi driver can configure the SPI clock rate by setting a divisor. The
+expressible divisors are all
+.B even
+numbers between 2 and 2^17 (=131072) resulting in SPI clock frequencies of
+6 MHz down to about 92 Hz for 12 MHz inputs. The default divisor is set to 2, but you can use another one by
+specifying the optional
+.B divisor
+parameter with the
+.sp
+.B " flashrom \-p ft2232_spi:divisor=div"
+.sp
+syntax.
+.SS
+.BR "serprog " programmer
+.IP
+This module supports all programmers speaking the serprog protocol. This includes some Arduino-based devices
+as well as various programmers by Urja Rannikko, Juhana Helovuo, Stefan Tauner, Chi Zhang and many others.
+.sp
+A mandatory parameter specifies either a serial device (and baud rate) or an IP/port combination for
+communicating with the programmer.
+The device/baud combination has to start with
+.B dev=
+and separate the optional baud rate with a colon.
+For example
+.sp
+.B " flashrom \-p serprog:dev=/dev/ttyS0:115200"
+.sp
+If no baud rate is given the default values by the operating system/hardware will be used.
+For IP connections you have to use the
+.sp
+.B " flashrom \-p serprog:ip=ipaddr:port"
+.sp
+syntax.
+In case the device supports it, you can set the SPI clock frequency with the optional
+.B spispeed
+parameter. The frequency is parsed as hertz, unless an
+.BR M ", or " k
+suffix is given, then megahertz or kilohertz are used respectively.
+Example that sets the frequency to 2 MHz:
+.sp
+.B " flashrom \-p serprog:dev=/dev/device:baud,spispeed=2M"
+.sp
+More information about serprog is available in
+.B serprog-protocol.txt
+in the source distribution.
+.SS
+.BR "buspirate_spi " programmer
+.IP
+A required
+.B dev
+parameter specifies the Bus Pirate device node and an optional
+.B spispeed
+parameter specifies the frequency of the SPI bus. The parameter
+delimiter is a comma. Syntax is
+.sp
+.B " flashrom \-p buspirate_spi:dev=/dev/device,spispeed=frequency"
+.sp
+where
+.B frequency
+can be
+.BR 30k ", " 125k ", " 250k ", " 1M ", " 2M ", " 2.6M ", " 4M " or " 8M
+(in Hz). The default is the maximum frequency of 8 MHz.
+.sp
+An optional pullups parameter specifies the use of the Bus Pirate internal pull-up resistors. This may be
+needed if you are working with a flash ROM chip that you have physically removed from the board. Syntax is
+.sp
+.B " flashrom -p buspirate_spi:pullups=state"
+.sp
+where
+.B state
+can be
+.BR on " or " off .
+More information about the Bus Pirate pull-up resistors and their purpose is available
+.URLB "http://dangerousprototypes.com/docs/Practical_guide_to_Bus_Pirate_pull-up_resistors" \
+"in a guide by dangerousprototypes" .
+Only the external supply voltage (Vpu) is supported as of this writing.
+.SS
+.BR "pickit2_spi " programmer
+.IP
+An optional
+.B voltage
+parameter specifies the voltage the PICkit2 should use. The default unit is Volt if no unit is specified.
+You can use
+.BR mV ", " millivolt ", " V " or " Volt
+as unit specifier. Syntax is
+.sp
+.B " flashrom \-p pickit2_spi:voltage=value"
+.sp
+where
+.B value
+can be
+.BR 0V ", " 1.8V ", " 2.5V ", " 3.5V
+or the equivalent in mV.
+.sp
+An optional
+.B spispeed
+parameter specifies the frequency of the SPI bus. Syntax is
+.sp
+.B " flashrom \-p pickit2_spi:spispeed=frequency"
+.sp
+where
+.B frequency
+can be
+.BR 250k ", " 333k ", " 500k " or " 1M "
+(in Hz). The default is a frequency of 1 MHz.
+.SS
+.BR "dediprog " programmer
+.IP
+An optional
+.B voltage
+parameter specifies the voltage the Dediprog should use. The default unit is
+Volt if no unit is specified. You can use
+.BR mV ", " milliVolt ", " V " or " Volt
+as unit specifier. Syntax is
+.sp
+.B " flashrom \-p dediprog:voltage=value"
+.sp
+where
+.B value
+can be
+.BR 0V ", " 1.8V ", " 2.5V ", " 3.5V
+or the equivalent in mV.
+.sp
+An optional
+.B device
+parameter specifies which of multiple connected Dediprog devices should be used.
+Please be aware that the order depends on libusb's usb_get_busses() function and that the numbering starts
+at 0.
+Usage example to select the second device:
+.sp
+.B " flashrom \-p dediprog:device=1"
+.sp
+An optional
+.B spispeed
+parameter specifies the frequency of the SPI bus. The firmware on the device needs to be 5.0.0 or newer.
+Syntax is
+.sp
+.B " flashrom \-p dediprog:spispeed=frequency"
+.sp
+where
+.B frequency
+can be
+.BR 375k ", " 750k ", " 1.5M ", " 2.18M ", " 3M ", " 8M ", " 12M " or " 24M
+(in Hz). The default is a frequency of 12 MHz.
+.sp
+An optional
+.B target
+parameter specifies which target chip should be used. Syntax is
+.sp
+.B " flashrom \-p dediprog:target=value"
+.sp
+where
+.B value
+can be
+.BR 1 " or " 2
+to select target chip 1 or 2 respectively. The default is target chip 1.
+.SS
+.BR "rayer_spi " programmer
+.IP
+The default I/O base address used for the parallel port is 0x378 and you can use
+the optional
+.B iobase
+parameter to specify an alternate base I/O address with the
+.sp
+.B " flashrom \-p rayer_spi:iobase=baseaddr"
+.sp
+syntax where
+.B baseaddr
+is base I/O port address of the parallel port, which must be a multiple of
+four. Make sure to not forget the "0x" prefix for hexadecimal port addresses.
+.sp
+The default cable type is the RayeR cable. You can use the optional
+.B type
+parameter to specify the cable type with the
+.sp
+.B " flashrom \-p rayer_spi:type=model"
+.sp
+syntax where
+.B model
+can be
+.BR rayer " for the RayeR cable, " byteblastermv " for the Altera ByteBlasterMV, " stk200 " for the Atmel \
+STK200/300, " wiggler " for the Macraigor Wiggler, " xilinx " for the Xilinx Parallel Cable III (DLC 5), or" \
+" spi_tt" " for SPI Tiny Tools-compatible hardware.
+.sp
+More information about the RayeR hardware is available at
+.nh
+.URLB "http://rayer.g6.cz/elektro/spipgm.htm" "RayeR's website" .
+The Altera ByteBlasterMV datasheet can be obtained from
+.URLB "http://www.altera.co.jp/literature/ds/dsbytemv.pdf" Altera .
+For more information about the Macraigor Wiggler see
+.URLB "http://www.macraigor.com/wiggler.htm" "their company homepage" .
+The schematic of the Xilinx DLC 5 was published in
+.URLB "http://www.xilinx.com/support/documentation/user_guides/xtp029.pdf" "a Xilinx user guide" .
+.SS
+.BR "pony_spi " programmer
+.IP
+The serial port (like /dev/ttyS0, /dev/ttyUSB0 on Linux or COM3 on windows) is
+specified using the mandatory
+.B dev
+parameter. The adapter type is selectable between SI-Prog (used for
+SPI devices with PonyProg 2000) or a custom made serial bitbanging programmer
+named "serbang". The optional
+.B type
+parameter accepts the values "si_prog" (default) or "serbang".
+.sp
+Information about the SI-Prog adapter can be found at
+.URLB "http://www.lancos.com/siprogsch.html" "its website" .
+.sp
+An example call to flashrom is
+.sp
+.B " flashrom \-p pony_spi:dev=/dev/ttyS0,type=serbang"
+.sp
+Please note that while USB-to-serial adapters work under certain circumstances,
+this slows down operation considerably.
+.SS
+.BR "ogp_spi " programmer
+.IP
+The flash ROM chip to access must be specified with the
+.B rom
+parameter.
+.sp
+.B " flashrom \-p ogp_spi:rom=name"
+.sp
+Where
+.B name
+is either
+.B cprom
+or
+.B s3
+for the configuration ROM and
+.B bprom
+or
+.B bios
+for the BIOS ROM. If more than one card supported by the ogp_spi programmer
+is installed in your system, you have to specify the PCI address of the card
+you want to use with the
+.B pci=
+parameter as explained in the
+.B nic3com et al.\&
+section above.
+.SS
+.BR "linux_spi " programmer
+.IP
+You have to specify the SPI controller to use with the
+.sp
+.B " flashrom \-p linux_spi:dev=/dev/spidevX.Y"
+.sp
+syntax where
+.B /dev/spidevX.Y
+is the Linux device node for your SPI controller.
+.sp
+In case the device supports it, you can set the SPI clock frequency with the optional
+.B spispeed
+parameter. The frequency is parsed as kilohertz.
+Example that sets the frequency to 8 MHz:
+.sp
+.B " flashrom \-p linux_spi:dev=/dev/spidevX.Y,spispeed=8000"
+.sp
+Please note that the linux_spi driver only works on Linux.
+.SS
+.BR "mstarddc_spi " programmer
+.IP
+The Display Data Channel (DDC) is an I2C bus present on VGA and DVI connectors, that allows exchanging
+information between a computer and attached displays. Its most common uses are getting display capabilities
+through EDID (at I2C address 0x50) and sending commands to the display using the DDC/CI protocol (at address
+0x37). On displays driven by MSTAR SoCs, it is also possible to access the SoC firmware flash (connected to
+the Soc through another SPI bus) using an In-System Programming (ISP) port, usually at address 0x49.
+This flashrom module allows the latter via Linux's I2C driver.
+.sp
+.B IMPORTANT:
+Before using this programmer, the display
+.B MUST
+be in standby mode, and only connected to the computer that will run flashrom using a VGA cable, to an
+inactive VGA output. It absolutely
+.B MUST NOT
+be used as a display during the procedure!
+.sp
+You have to specify the DDC/I2C controller and I2C address to use with the
+.sp
+.B " flashrom \-p mstarddc_spi:dev=/dev/i2c-X:YY"
+.sp
+syntax where
+.B /dev/i2c-X
+is the Linux device node for your I2C controller connected to the display's DDC channel, and
+.B YY
+is the (hexadecimal) address of the MSTAR ISP port (address 0x49 is usually used).
+Example that uses I2C controller /dev/i2c-1 and address 0x49:
+.sp
+.B " flashrom \-p mstarddc_spi:dev=/dev/i2c-1:49
+.sp
+It is also possible to inhibit the reset command that is normally sent to the display once the flashrom
+operation is completed using the optional
+.B noreset
+parameter. A value of 1 prevents flashrom from sending the reset command.
+Example that does not reset the display at the end of the operation:
+.sp
+.B " flashrom \-p mstarddc_spi:dev=/dev/i2c-1:49,noreset=1
+.sp
+Please note that sending the reset command is also inhibited if an error occurred during the operation.
+To send the reset command afterwards, you can simply run flashrom once more, in chip probe mode (not specifying
+an operation), without the
+.B noreset
+parameter, once the flash read/write operation you intended to perform has completed successfully.
+.sp
+Please also note that the mstarddc_spi driver only works on Linux.
+.SS
+.BR "ch341a_spi " programmer
+The WCH CH341A programmer does not support any parameters currently. SPI frequency is fixed at 2 MHz, and CS0 is
+used as per the device.
+.SH EXAMPLES
+To back up and update your BIOS, run
+.sp
+.B flashrom -p internal -r backup.rom -o backuplog.txt
+.br
+.B flashrom -p internal -w newbios.rom -o writelog.txt
+.sp
+Please make sure to copy backup.rom to some external media before you try
+to write. That makes offline recovery easier.
+.br
+If writing fails and flashrom complains about the chip being in an unknown
+state, you can try to restore the backup by running
+.sp
+.B flashrom -p internal -w backup.rom -o restorelog.txt
+.sp
+If you encounter any problems, please contact us and supply
+backuplog.txt, writelog.txt and restorelog.txt. See section
+.B BUGS
+for contact info.
+.SH EXIT STATUS
+flashrom exits with 0 on success, 1 on most failures but with 3 if a call to mmap() fails.
+.SH REQUIREMENTS
+flashrom needs different access permissions for different programmers.
+.sp
+.B internal
+needs raw memory access, PCI configuration space access, raw I/O port
+access (x86) and MSR access (x86).
+.sp
+.B atavia
+needs PCI configuration space access.
+.sp
+.BR nic3com ", " nicrealtek " and " nicnatsemi "
+need PCI configuration space read access and raw I/O port access.
+.sp
+.B atahpt
+needs PCI configuration space access and raw I/O port access.
+.sp
+.BR gfxnvidia ", " drkaiser " and " it8212
+need PCI configuration space access and raw memory access.
+.sp
+.B rayer_spi
+needs raw I/O port access.
+.sp
+.BR satasii ", " nicintel ", " nicintel_eeprom " and " nicintel_spi
+need PCI configuration space read access and raw memory access.
+.sp
+.BR satamv " and " atapromise
+need PCI configuration space read access, raw I/O port access and raw memory
+access.
+.sp
+.B serprog
+needs TCP access to the network or userspace access to a serial port.
+.sp
+.B buspirate_spi
+needs userspace access to a serial port.
+.sp
+.BR ft2232_spi ", " usbblaster_spi " and " pickit2_spi
+need access to the respective USB device via libusb API version 0.1.
+.sp
+.BR ch341a_spi " and " dediprog
+need access to the respective USB device via libusb API version 1.0.
+.sp
+.B dummy
+needs no access permissions at all.
+.sp
+.BR internal ", " nic3com ", " nicrealtek ", " nicnatsemi ", "
+.BR gfxnvidia ", " drkaiser ", " satasii ", " satamv ", " atahpt ", " atavia " and " atapromise
+have to be run as superuser/root, and need additional raw access permission.
+.sp
+.BR serprog ", " buspirate_spi ", " dediprog ", " usbblaster_spi ", " ft2232_spi ", " pickit2_spi " and " \
+ch341a_spi
+can be run as normal user on most operating systems if appropriate device
+permissions are set.
+.sp
+.B ogp
+needs PCI configuration space read access and raw memory access.
+.sp
+On OpenBSD, you can obtain raw access permission by setting
+.B "securelevel=-1"
+in
+.B "/etc/rc.securelevel"
+and rebooting, or rebooting into single user mode.
+.SH BUGS
+Please report any bugs to the
+.MTOB "flashrom@flashrom.org" "flashrom mailing list" .
+.sp
+We recommend to subscribe first at
+.URLB "https://flashrom.org/mailman/listinfo/flashrom" "" .
+.sp
+Many of the developers communicate via the
+.B "#flashrom"
+IRC channel on
+.BR chat.freenode.net .
+If you don't have an IRC client, you can use the
+.URLB http://webchat.freenode.net/?channels=flashrom "freenode webchat" .
+You are welcome to join and ask questions, send us bug and success reports there
+too. Please provide a way to contact you later (e.g.\& a mail address) and be
+patient if there is no immediate reaction. Also, we provide a
+.URLB https://paste.flashrom.org "pastebin service"
+that is very useful when you want to share logs etc.\& without spamming the
+channel.
+.SS
+.B Laptops
+.sp
+Using flashrom on laptops is dangerous and may easily make your hardware
+unusable. flashrom will attempt to detect if it is running on a laptop and abort
+immediately for safety reasons. Please see the detailed discussion of this topic
+and associated flashrom options in the
+.B Laptops
+paragraph in the
+.B internal programmer
+subsection of the
+.B PROGRAMMER-SPECIFIC INFORMATION
+section and the information
+.URLB "https://flashrom.org/Laptops" "in our wiki" .
+.SS
+One-time programmable (OTP) memory and unique IDs
+.sp
+Some flash chips contain OTP memory often denoted as "security registers".
+They usually have a capacity in the range of some bytes to a few hundred
+bytes and can be used to give devices unique IDs etc. flashrom is not able
+to read or write these memories and may therefore not be able to duplicate a
+chip completely. For chip types known to include OTP memories a warning is
+printed when they are detected.
+.sp
+Similar to OTP memories are unique, factory programmed, unforgeable IDs.
+They are not modifiable by the user at all.
+.SH LICENSE
+.B flashrom
+is covered by the GNU General Public License (GPL), version 2. Some files are
+additionally available under any later version of the GPL.
+.SH COPYRIGHT
+.br
+Please see the individual files.
+.SH AUTHORS
+Andrew Morgan
+.br
+Carl-Daniel Hailfinger
+.br
+Claus Gindhart
+.br
+David Borg
+.br
+David Hendricks
+.br
+Dominik Geyer
+.br
+Eric Biederman
+.br
+Giampiero Giancipoli
+.br
+Helge Wagner
+.br
+Idwer Vollering
+.br
+Joe Bao
+.br
+Joerg Fischer
+.br
+Joshua Roys
+.br
+Ky\[:o]sti M\[:a]lkki
+.br
+Luc Verhaegen
+.br
+Li-Ta Lo
+.br
+Mark Marshall
+.br
+Markus Boas
+.br
+Mattias Mattsson
+.br
+Michael Karcher
+.br
+Nikolay Petukhov
+.br
+Patrick Georgi
+.br
+Peter Lemenkov
+.br
+Peter Stuge
+.br
+Reinder E.N. de Haan
+.br
+Ronald G. Minnich
+.br
+Ronald Hoogenboom
+.br
+Sean Nelson
+.br
+Stefan Reinauer
+.br
+Stefan Tauner
+.br
+Stefan Wildemann
+.br
+Stephan Guilloux
+.br
+Steven James
+.br
+Urja Rannikko
+.br
+Uwe Hermann
+.br
+Wang Qingpei
+.br
+Yinghai Lu
+.br
+some others, please see the flashrom svn changelog for details.
+.br
+All still active authors can be reached via
+.MTOB "flashrom@flashrom.org" "the mailing list" .
+.PP
+This manual page was written by
+.MTOB "uwe@hermann-uwe.de" "Uwe Hermann" ,
+Carl-Daniel Hailfinger, Stefan Tauner and others.
+It is licensed under the terms of the GNU GPL (version 2 or later).
diff --git a/flashrom.c b/flashrom.c
new file mode 100644
index 0000000..25e53f2
--- /dev/null
+++ b/flashrom.c
@@ -0,0 +1,2136 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifndef __LIBPAYLOAD__
+#include <fcntl.h>
+#include <sys/stat.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+#if HAVE_UTSNAME == 1
+#include <sys/utsname.h>
+#endif
+#include "flash.h"
+#include "flashchips.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+const char flashrom_version[] = FLASHROM_VERSION;
+const char *chip_to_probe = NULL;
+
+static enum programmer programmer = PROGRAMMER_INVALID;
+static const char *programmer_param = NULL;
+
+/*
+ * Programmers supporting multiple buses can have differing size limits on
+ * each bus. Store the limits for each bus in a common struct.
+ */
+struct decode_sizes max_rom_decode;
+
+/* If nonzero, used as the start address of bottom-aligned flash. */
+unsigned long flashbase;
+
+/* Is writing allowed with this programmer? */
+int programmer_may_write;
+
+const struct programmer_entry programmer_table[] = {
+#if CONFIG_INTERNAL == 1
+ {
+ .name = "internal",
+ .type = OTHER,
+ .devs.note = NULL,
+ .init = internal_init,
+ .map_flash_region = physmap,
+ .unmap_flash_region = physunmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_DUMMY == 1
+ {
+ .name = "dummy",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "Dummy device, does nothing and logs all accesses\n",
+ .init = dummy_init,
+ .map_flash_region = dummy_map,
+ .unmap_flash_region = dummy_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NIC3COM == 1
+ {
+ .name = "nic3com",
+ .type = PCI,
+ .devs.dev = nics_3com,
+ .init = nic3com_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NICREALTEK == 1
+ {
+ /* This programmer works for Realtek RTL8139 and SMC 1211. */
+ .name = "nicrealtek",
+ .type = PCI,
+ .devs.dev = nics_realtek,
+ .init = nicrealtek_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NICNATSEMI == 1
+ {
+ .name = "nicnatsemi",
+ .type = PCI,
+ .devs.dev = nics_natsemi,
+ .init = nicnatsemi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_GFXNVIDIA == 1
+ {
+ .name = "gfxnvidia",
+ .type = PCI,
+ .devs.dev = gfx_nvidia,
+ .init = gfxnvidia_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_DRKAISER == 1
+ {
+ .name = "drkaiser",
+ .type = PCI,
+ .devs.dev = drkaiser_pcidev,
+ .init = drkaiser_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_SATASII == 1
+ {
+ .name = "satasii",
+ .type = PCI,
+ .devs.dev = satas_sii,
+ .init = satasii_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_ATAHPT == 1
+ {
+ .name = "atahpt",
+ .type = PCI,
+ .devs.dev = ata_hpt,
+ .init = atahpt_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_ATAVIA == 1
+ {
+ .name = "atavia",
+ .type = PCI,
+ .devs.dev = ata_via,
+ .init = atavia_init,
+ .map_flash_region = atavia_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_ATAPROMISE == 1
+ {
+ .name = "atapromise",
+ .type = PCI,
+ .devs.dev = ata_promise,
+ .init = atapromise_init,
+ .map_flash_region = atapromise_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_IT8212 == 1
+ {
+ .name = "it8212",
+ .type = PCI,
+ .devs.dev = devs_it8212,
+ .init = it8212_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_FT2232_SPI == 1
+ {
+ .name = "ft2232_spi",
+ .type = USB,
+ .devs.dev = devs_ft2232spi,
+ .init = ft2232_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_SERPROG == 1
+ {
+ .name = "serprog",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "All programmer devices speaking the serprog protocol\n",
+ .init = serprog_init,
+ .map_flash_region = serprog_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = serprog_delay,
+ },
+#endif
+
+#if CONFIG_BUSPIRATE_SPI == 1
+ {
+ .name = "buspirate_spi",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "Dangerous Prototypes Bus Pirate\n",
+ .init = buspirate_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_DEDIPROG == 1
+ {
+ .name = "dediprog",
+ .type = USB,
+ .devs.dev = devs_dediprog,
+ .init = dediprog_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_RAYER_SPI == 1
+ {
+ .name = "rayer_spi",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "RayeR parallel port programmer\n",
+ .init = rayer_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_PONY_SPI == 1
+ {
+ .name = "pony_spi",
+ .type = OTHER,
+ /* FIXME */
+ .devs.note = "Programmers compatible with SI-Prog, serbang or AJAWe\n",
+ .init = pony_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NICINTEL == 1
+ {
+ .name = "nicintel",
+ .type = PCI,
+ .devs.dev = nics_intel,
+ .init = nicintel_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NICINTEL_SPI == 1
+ {
+ .name = "nicintel_spi",
+ .type = PCI,
+ .devs.dev = nics_intel_spi,
+ .init = nicintel_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_NICINTEL_EEPROM == 1
+ {
+ .name = "nicintel_eeprom",
+ .type = PCI,
+ .devs.dev = nics_intel_ee,
+ .init = nicintel_ee_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_OGP_SPI == 1
+ {
+ .name = "ogp_spi",
+ .type = PCI,
+ .devs.dev = ogp_spi,
+ .init = ogp_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_SATAMV == 1
+ {
+ .name = "satamv",
+ .type = PCI,
+ .devs.dev = satas_mv,
+ .init = satamv_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_LINUX_SPI == 1
+ {
+ .name = "linux_spi",
+ .type = OTHER,
+ .devs.note = "Device files /dev/spidev*.*\n",
+ .init = linux_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_USBBLASTER_SPI == 1
+ {
+ .name = "usbblaster_spi",
+ .type = USB,
+ .devs.dev = devs_usbblasterspi,
+ .init = usbblaster_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_MSTARDDC_SPI == 1
+ {
+ .name = "mstarddc_spi",
+ .type = OTHER,
+ .devs.note = "MSTAR DDC devices addressable via /dev/i2c-* on Linux.\n",
+ .init = mstarddc_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_PICKIT2_SPI == 1
+ {
+ .name = "pickit2_spi",
+ .type = USB,
+ .devs.dev = devs_pickit2_spi,
+ .init = pickit2_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = internal_delay,
+ },
+#endif
+
+#if CONFIG_CH341A_SPI == 1
+ {
+ .name = "ch341a_spi",
+ .type = USB,
+ .devs.dev = devs_ch341a_spi,
+ .init = ch341a_spi_init,
+ .map_flash_region = fallback_map,
+ .unmap_flash_region = fallback_unmap,
+ .delay = ch341a_spi_delay,
+ },
+#endif
+
+ {0}, /* This entry corresponds to PROGRAMMER_INVALID. */
+};
+
+#define SHUTDOWN_MAXFN 32
+static int shutdown_fn_count = 0;
+struct shutdown_func_data {
+ int (*func) (void *data);
+ void *data;
+} static shutdown_fn[SHUTDOWN_MAXFN];
+/* Initialize to 0 to make sure nobody registers a shutdown function before
+ * programmer init.
+ */
+static int may_register_shutdown = 0;
+
+/* Did we change something or was every erase/write skipped (if any)? */
+static bool all_skipped = true;
+
+static int check_block_eraser(const struct flashctx *flash, int k, int log);
+
+int shutdown_free(void *data)
+{
+ free(data);
+ return 0;
+}
+
+/* Register a function to be executed on programmer shutdown.
+ * The advantage over atexit() is that you can supply a void pointer which will
+ * be used as parameter to the registered function upon programmer shutdown.
+ * This pointer can point to arbitrary data used by said function, e.g. undo
+ * information for GPIO settings etc. If unneeded, set data=NULL.
+ * Please note that the first (void *data) belongs to the function signature of
+ * the function passed as first parameter.
+ */
+int register_shutdown(int (*function) (void *data), void *data)
+{
+ if (shutdown_fn_count >= SHUTDOWN_MAXFN) {
+ msg_perr("Tried to register more than %i shutdown functions.\n",
+ SHUTDOWN_MAXFN);
+ return 1;
+ }
+ if (!may_register_shutdown) {
+ msg_perr("Tried to register a shutdown function before "
+ "programmer init.\n");
+ return 1;
+ }
+ shutdown_fn[shutdown_fn_count].func = function;
+ shutdown_fn[shutdown_fn_count].data = data;
+ shutdown_fn_count++;
+
+ return 0;
+}
+
+int programmer_init(enum programmer prog, const char *param)
+{
+ int ret;
+
+ if (prog >= PROGRAMMER_INVALID) {
+ msg_perr("Invalid programmer specified!\n");
+ return -1;
+ }
+ programmer = prog;
+ /* Initialize all programmer specific data. */
+ /* Default to unlimited decode sizes. */
+ max_rom_decode = (const struct decode_sizes) {
+ .parallel = 0xffffffff,
+ .lpc = 0xffffffff,
+ .fwh = 0xffffffff,
+ .spi = 0xffffffff,
+ };
+ /* Default to top aligned flash at 4 GB. */
+ flashbase = 0;
+ /* Registering shutdown functions is now allowed. */
+ may_register_shutdown = 1;
+ /* Default to allowing writes. Broken programmers set this to 0. */
+ programmer_may_write = 1;
+
+ programmer_param = param;
+ msg_pdbg("Initializing %s programmer\n", programmer_table[programmer].name);
+ ret = programmer_table[programmer].init();
+ if (programmer_param && strlen(programmer_param)) {
+ if (ret != 0) {
+ /* It is quite possible that any unhandled programmer parameter would have been valid,
+ * but an error in actual programmer init happened before the parameter was evaluated.
+ */
+ msg_pwarn("Unhandled programmer parameters (possibly due to another failure): %s\n",
+ programmer_param);
+ } else {
+ /* Actual programmer init was successful, but the user specified an invalid or unusable
+ * (for the current programmer configuration) parameter.
+ */
+ msg_perr("Unhandled programmer parameters: %s\n", programmer_param);
+ msg_perr("Aborting.\n");
+ ret = ERROR_FATAL;
+ }
+ }
+ return ret;
+}
+
+/** Calls registered shutdown functions and resets internal programmer-related variables.
+ * Calling it is safe even without previous initialization, but further interactions with programmer support
+ * require a call to programmer_init() (afterwards).
+ *
+ * @return The OR-ed result values of all shutdown functions (i.e. 0 on success). */
+int programmer_shutdown(void)
+{
+ int ret = 0;
+
+ /* Registering shutdown functions is no longer allowed. */
+ may_register_shutdown = 0;
+ while (shutdown_fn_count > 0) {
+ int i = --shutdown_fn_count;
+ ret |= shutdown_fn[i].func(shutdown_fn[i].data);
+ }
+
+ programmer_param = NULL;
+ registered_master_count = 0;
+
+ return ret;
+}
+
+void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ void *ret = programmer_table[programmer].map_flash_region(descr, phys_addr, len);
+ msg_gspew("%s: mapping %s from 0x%0*" PRIxPTR " to 0x%0*" PRIxPTR "\n",
+ __func__, descr, PRIxPTR_WIDTH, phys_addr, PRIxPTR_WIDTH, (uintptr_t) ret);
+ return ret;
+}
+
+void programmer_unmap_flash_region(void *virt_addr, size_t len)
+{
+ programmer_table[programmer].unmap_flash_region(virt_addr, len);
+ msg_gspew("%s: unmapped 0x%0*" PRIxPTR "\n", __func__, PRIxPTR_WIDTH, (uintptr_t)virt_addr);
+}
+
+void chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ flash->mst->par.chip_writeb(flash, val, addr);
+}
+
+void chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr)
+{
+ flash->mst->par.chip_writew(flash, val, addr);
+}
+
+void chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr)
+{
+ flash->mst->par.chip_writel(flash, val, addr);
+}
+
+void chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len)
+{
+ flash->mst->par.chip_writen(flash, buf, addr, len);
+}
+
+uint8_t chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ return flash->mst->par.chip_readb(flash, addr);
+}
+
+uint16_t chip_readw(const struct flashctx *flash, const chipaddr addr)
+{
+ return flash->mst->par.chip_readw(flash, addr);
+}
+
+uint32_t chip_readl(const struct flashctx *flash, const chipaddr addr)
+{
+ return flash->mst->par.chip_readl(flash, addr);
+}
+
+void chip_readn(const struct flashctx *flash, uint8_t *buf, chipaddr addr,
+ size_t len)
+{
+ flash->mst->par.chip_readn(flash, buf, addr, len);
+}
+
+void programmer_delay(unsigned int usecs)
+{
+ if (usecs > 0)
+ programmer_table[programmer].delay(usecs);
+}
+
+int read_memmapped(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ int unsigned len)
+{
+ chip_readn(flash, buf, flash->virtual_memory + start, len);
+
+ return 0;
+}
+
+/* This is a somewhat hacked function similar in some ways to strtok().
+ * It will look for needle with a subsequent '=' in haystack, return a copy of
+ * needle and remove everything from the first occurrence of needle to the next
+ * delimiter from haystack.
+ */
+char *extract_param(const char *const *haystack, const char *needle, const char *delim)
+{
+ char *param_pos, *opt_pos, *rest;
+ char *opt = NULL;
+ int optlen;
+ int needlelen;
+
+ needlelen = strlen(needle);
+ if (!needlelen) {
+ msg_gerr("%s: empty needle! Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ return NULL;
+ }
+ /* No programmer parameters given. */
+ if (*haystack == NULL)
+ return NULL;
+ param_pos = strstr(*haystack, needle);
+ do {
+ if (!param_pos)
+ return NULL;
+ /* Needle followed by '='? */
+ if (param_pos[needlelen] == '=') {
+
+ /* Beginning of the string? */
+ if (param_pos == *haystack)
+ break;
+ /* After a delimiter? */
+ if (strchr(delim, *(param_pos - 1)))
+ break;
+ }
+ /* Continue searching. */
+ param_pos++;
+ param_pos = strstr(param_pos, needle);
+ } while (1);
+
+ if (param_pos) {
+ /* Get the string after needle and '='. */
+ opt_pos = param_pos + needlelen + 1;
+ optlen = strcspn(opt_pos, delim);
+ /* Return an empty string if the parameter was empty. */
+ opt = malloc(optlen + 1);
+ if (!opt) {
+ msg_gerr("Out of memory!\n");
+ exit(1);
+ }
+ strncpy(opt, opt_pos, optlen);
+ opt[optlen] = '\0';
+ rest = opt_pos + optlen;
+ /* Skip all delimiters after the current parameter. */
+ rest += strspn(rest, delim);
+ memmove(param_pos, rest, strlen(rest) + 1);
+ /* We could shrink haystack, but the effort is not worth it. */
+ }
+
+ return opt;
+}
+
+char *extract_programmer_param(const char *param_name)
+{
+ return extract_param(&programmer_param, param_name, ",");
+}
+
+/* Returns the number of well-defined erasers for a chip. */
+static unsigned int count_usable_erasers(const struct flashctx *flash)
+{
+ unsigned int usable_erasefunctions = 0;
+ int k;
+ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+ if (!check_block_eraser(flash, k, 0))
+ usable_erasefunctions++;
+ }
+ return usable_erasefunctions;
+}
+
+static int compare_range(const uint8_t *wantbuf, const uint8_t *havebuf, unsigned int start, unsigned int len)
+{
+ int ret = 0, failcount = 0;
+ unsigned int i;
+ for (i = 0; i < len; i++) {
+ if (wantbuf[i] != havebuf[i]) {
+ /* Only print the first failure. */
+ if (!failcount++)
+ msg_cerr("FAILED at 0x%08x! Expected=0x%02x, Found=0x%02x,",
+ start + i, wantbuf[i], havebuf[i]);
+ }
+ }
+ if (failcount) {
+ msg_cerr(" failed byte count from 0x%08x-0x%08x: 0x%x\n",
+ start, start + len - 1, failcount);
+ ret = -1;
+ }
+ return ret;
+}
+
+/* start is an offset to the base address of the flash chip */
+int check_erased_range(struct flashctx *flash, unsigned int start,
+ unsigned int len)
+{
+ int ret;
+ uint8_t *cmpbuf = malloc(len);
+
+ if (!cmpbuf) {
+ msg_gerr("Could not allocate memory!\n");
+ exit(1);
+ }
+ memset(cmpbuf, 0xff, len);
+ ret = verify_range(flash, cmpbuf, start, len);
+ free(cmpbuf);
+ return ret;
+}
+
+/*
+ * @cmpbuf buffer to compare against, cmpbuf[0] is expected to match the
+ * flash content at location start
+ * @start offset to the base address of the flash chip
+ * @len length of the verified area
+ * @return 0 for success, -1 for failure
+ */
+int verify_range(struct flashctx *flash, const uint8_t *cmpbuf, unsigned int start, unsigned int len)
+{
+ if (!len)
+ return -1;
+
+ if (!flash->chip->read) {
+ msg_cerr("ERROR: flashrom has no read function for this flash chip.\n");
+ return -1;
+ }
+
+ uint8_t *readbuf = malloc(len);
+ if (!readbuf) {
+ msg_gerr("Could not allocate memory!\n");
+ return -1;
+ }
+ int ret = 0;
+
+ if (start + len > flash->chip->total_size * 1024) {
+ msg_gerr("Error: %s called with start 0x%x + len 0x%x >"
+ " total_size 0x%x\n", __func__, start, len,
+ flash->chip->total_size * 1024);
+ ret = -1;
+ goto out_free;
+ }
+
+ ret = flash->chip->read(flash, readbuf, start, len);
+ if (ret) {
+ msg_gerr("Verification impossible because read failed "
+ "at 0x%x (len 0x%x)\n", start, len);
+ ret = -1;
+ goto out_free;
+ }
+
+ ret = compare_range(cmpbuf, readbuf, start, len);
+out_free:
+ free(readbuf);
+ return ret;
+}
+
+/* Helper function for need_erase() that focuses on granularities of gran bytes. */
+static int need_erase_gran_bytes(const uint8_t *have, const uint8_t *want, unsigned int len, unsigned int gran)
+{
+ unsigned int i, j, limit;
+ for (j = 0; j < len / gran; j++) {
+ limit = min (gran, len - j * gran);
+ /* Are 'have' and 'want' identical? */
+ if (!memcmp(have + j * gran, want + j * gran, limit))
+ continue;
+ /* have needs to be in erased state. */
+ for (i = 0; i < limit; i++)
+ if (have[j * gran + i] != 0xff)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Check if the buffer @have can be programmed to the content of @want without
+ * erasing. This is only possible if all chunks of size @gran are either kept
+ * as-is or changed from an all-ones state to any other state.
+ *
+ * Warning: This function assumes that @have and @want point to naturally
+ * aligned regions.
+ *
+ * @have buffer with current content
+ * @want buffer with desired content
+ * @len length of the checked area
+ * @gran write granularity (enum, not count)
+ * @return 0 if no erase is needed, 1 otherwise
+ */
+int need_erase(const uint8_t *have, const uint8_t *want, unsigned int len, enum write_granularity gran)
+{
+ int result = 0;
+ unsigned int i;
+
+ switch (gran) {
+ case write_gran_1bit:
+ for (i = 0; i < len; i++)
+ if ((have[i] & want[i]) != want[i]) {
+ result = 1;
+ break;
+ }
+ break;
+ case write_gran_1byte:
+ for (i = 0; i < len; i++)
+ if ((have[i] != want[i]) && (have[i] != 0xff)) {
+ result = 1;
+ break;
+ }
+ break;
+ case write_gran_128bytes:
+ result = need_erase_gran_bytes(have, want, len, 128);
+ break;
+ case write_gran_256bytes:
+ result = need_erase_gran_bytes(have, want, len, 256);
+ break;
+ case write_gran_264bytes:
+ result = need_erase_gran_bytes(have, want, len, 264);
+ break;
+ case write_gran_512bytes:
+ result = need_erase_gran_bytes(have, want, len, 512);
+ break;
+ case write_gran_528bytes:
+ result = need_erase_gran_bytes(have, want, len, 528);
+ break;
+ case write_gran_1024bytes:
+ result = need_erase_gran_bytes(have, want, len, 1024);
+ break;
+ case write_gran_1056bytes:
+ result = need_erase_gran_bytes(have, want, len, 1056);
+ break;
+ case write_gran_1byte_implicit_erase:
+ /* Do not erase, handle content changes from anything->0xff by writing 0xff. */
+ result = 0;
+ break;
+ default:
+ msg_cerr("%s: Unsupported granularity! Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ }
+ return result;
+}
+
+/**
+ * Check if the buffer @have needs to be programmed to get the content of @want.
+ * If yes, return 1 and fill in first_start with the start address of the
+ * write operation and first_len with the length of the first to-be-written
+ * chunk. If not, return 0 and leave first_start and first_len undefined.
+ *
+ * Warning: This function assumes that @have and @want point to naturally
+ * aligned regions.
+ *
+ * @have buffer with current content
+ * @want buffer with desired content
+ * @len length of the checked area
+ * @gran write granularity (enum, not count)
+ * @first_start offset of the first byte which needs to be written (passed in
+ * value is increased by the offset of the first needed write
+ * relative to have/want or unchanged if no write is needed)
+ * @return length of the first contiguous area which needs to be written
+ * 0 if no write is needed
+ *
+ * FIXME: This function needs a parameter which tells it about coalescing
+ * in relation to the max write length of the programmer and the max write
+ * length of the chip.
+ */
+static unsigned int get_next_write(const uint8_t *have, const uint8_t *want, unsigned int len,
+ unsigned int *first_start,
+ enum write_granularity gran)
+{
+ int need_write = 0;
+ unsigned int rel_start = 0, first_len = 0;
+ unsigned int i, limit, stride;
+
+ switch (gran) {
+ case write_gran_1bit:
+ case write_gran_1byte:
+ case write_gran_1byte_implicit_erase:
+ stride = 1;
+ break;
+ case write_gran_128bytes:
+ stride = 128;
+ break;
+ case write_gran_256bytes:
+ stride = 256;
+ break;
+ case write_gran_264bytes:
+ stride = 264;
+ break;
+ case write_gran_512bytes:
+ stride = 512;
+ break;
+ case write_gran_528bytes:
+ stride = 528;
+ break;
+ case write_gran_1024bytes:
+ stride = 1024;
+ break;
+ case write_gran_1056bytes:
+ stride = 1056;
+ break;
+ default:
+ msg_cerr("%s: Unsupported granularity! Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ /* Claim that no write was needed. A write with unknown
+ * granularity is too dangerous to try.
+ */
+ return 0;
+ }
+ for (i = 0; i < len / stride; i++) {
+ limit = min(stride, len - i * stride);
+ /* Are 'have' and 'want' identical? */
+ if (memcmp(have + i * stride, want + i * stride, limit)) {
+ if (!need_write) {
+ /* First location where have and want differ. */
+ need_write = 1;
+ rel_start = i * stride;
+ }
+ } else {
+ if (need_write) {
+ /* First location where have and want
+ * do not differ anymore.
+ */
+ break;
+ }
+ }
+ }
+ if (need_write)
+ first_len = min(i * stride - rel_start, len);
+ *first_start += rel_start;
+ return first_len;
+}
+
+/* This function generates various test patterns useful for testing controller
+ * and chip communication as well as chip behaviour.
+ *
+ * If a byte can be written multiple times, each time keeping 0-bits at 0
+ * and changing 1-bits to 0 if the new value for that bit is 0, the effect
+ * is essentially an AND operation. That's also the reason why this function
+ * provides the result of AND between various patterns.
+ *
+ * Below is a list of patterns (and their block length).
+ * Pattern 0 is 05 15 25 35 45 55 65 75 85 95 a5 b5 c5 d5 e5 f5 (16 Bytes)
+ * Pattern 1 is 0a 1a 2a 3a 4a 5a 6a 7a 8a 9a aa ba ca da ea fa (16 Bytes)
+ * Pattern 2 is 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f (16 Bytes)
+ * Pattern 3 is a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af (16 Bytes)
+ * Pattern 4 is 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 (16 Bytes)
+ * Pattern 5 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f (16 Bytes)
+ * Pattern 6 is 00 (1 Byte)
+ * Pattern 7 is ff (1 Byte)
+ * Patterns 0-7 have a big-endian block number in the last 2 bytes of each 256
+ * byte block.
+ *
+ * Pattern 8 is 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11... (256 B)
+ * Pattern 9 is ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0 ef ee... (256 B)
+ * Pattern 10 is 00 00 00 01 00 02 00 03 00 04... (128 kB big-endian counter)
+ * Pattern 11 is ff ff ff fe ff fd ff fc ff fb... (128 kB big-endian downwards)
+ * Pattern 12 is 00 (1 Byte)
+ * Pattern 13 is ff (1 Byte)
+ * Patterns 8-13 have no block number.
+ *
+ * Patterns 0-3 are created to detect and efficiently diagnose communication
+ * slips like missed bits or bytes and their repetitive nature gives good visual
+ * cues to the person inspecting the results. In addition, the following holds:
+ * AND Pattern 0/1 == Pattern 4
+ * AND Pattern 2/3 == Pattern 5
+ * AND Pattern 0/1/2/3 == AND Pattern 4/5 == Pattern 6
+ * A weakness of pattern 0-5 is the inability to detect swaps/copies between
+ * any two 16-byte blocks except for the last 16-byte block in a 256-byte bloc.
+ * They work perfectly for detecting any swaps/aliasing of blocks >= 256 bytes.
+ * 0x5 and 0xa were picked because they are 0101 and 1010 binary.
+ * Patterns 8-9 are best for detecting swaps/aliasing of blocks < 256 bytes.
+ * Besides that, they provide for bit testing of the last two bytes of every
+ * 256 byte block which contains the block number for patterns 0-6.
+ * Patterns 10-11 are special purpose for detecting subblock aliasing with
+ * block sizes >256 bytes (some Dataflash chips etc.)
+ * AND Pattern 8/9 == Pattern 12
+ * AND Pattern 10/11 == Pattern 12
+ * Pattern 13 is the completely erased state.
+ * None of the patterns can detect aliasing at boundaries which are a multiple
+ * of 16 MBytes (but such chips do not exist anyway for Parallel/LPC/FWH/SPI).
+ */
+int generate_testpattern(uint8_t *buf, uint32_t size, int variant)
+{
+ int i;
+
+ if (!buf) {
+ msg_gerr("Invalid buffer!\n");
+ return 1;
+ }
+
+ switch (variant) {
+ case 0:
+ for (i = 0; i < size; i++)
+ buf[i] = (i & 0xf) << 4 | 0x5;
+ break;
+ case 1:
+ for (i = 0; i < size; i++)
+ buf[i] = (i & 0xf) << 4 | 0xa;
+ break;
+ case 2:
+ for (i = 0; i < size; i++)
+ buf[i] = 0x50 | (i & 0xf);
+ break;
+ case 3:
+ for (i = 0; i < size; i++)
+ buf[i] = 0xa0 | (i & 0xf);
+ break;
+ case 4:
+ for (i = 0; i < size; i++)
+ buf[i] = (i & 0xf) << 4;
+ break;
+ case 5:
+ for (i = 0; i < size; i++)
+ buf[i] = i & 0xf;
+ break;
+ case 6:
+ memset(buf, 0x00, size);
+ break;
+ case 7:
+ memset(buf, 0xff, size);
+ break;
+ case 8:
+ for (i = 0; i < size; i++)
+ buf[i] = i & 0xff;
+ break;
+ case 9:
+ for (i = 0; i < size; i++)
+ buf[i] = ~(i & 0xff);
+ break;
+ case 10:
+ for (i = 0; i < size % 2; i++) {
+ buf[i * 2] = (i >> 8) & 0xff;
+ buf[i * 2 + 1] = i & 0xff;
+ }
+ if (size & 0x1)
+ buf[i * 2] = (i >> 8) & 0xff;
+ break;
+ case 11:
+ for (i = 0; i < size % 2; i++) {
+ buf[i * 2] = ~((i >> 8) & 0xff);
+ buf[i * 2 + 1] = ~(i & 0xff);
+ }
+ if (size & 0x1)
+ buf[i * 2] = ~((i >> 8) & 0xff);
+ break;
+ case 12:
+ memset(buf, 0x00, size);
+ break;
+ case 13:
+ memset(buf, 0xff, size);
+ break;
+ }
+
+ if ((variant >= 0) && (variant <= 7)) {
+ /* Write block number in the last two bytes of each 256-byte
+ * block, big endian for easier reading of the hexdump.
+ * Note that this wraps around for chips larger than 2^24 bytes
+ * (16 MB).
+ */
+ for (i = 0; i < size / 256; i++) {
+ buf[i * 256 + 254] = (i >> 8) & 0xff;
+ buf[i * 256 + 255] = i & 0xff;
+ }
+ }
+
+ return 0;
+}
+
+/* Returns the number of busses commonly supported by the current programmer and flash chip where the latter
+ * can not be completely accessed due to size/address limits of the programmer. */
+unsigned int count_max_decode_exceedings(const struct flashctx *flash)
+{
+ unsigned int limitexceeded = 0;
+ uint32_t size = flash->chip->total_size * 1024;
+ enum chipbustype buses = flash->mst->buses_supported & flash->chip->bustype;
+
+ if ((buses & BUS_PARALLEL) && (max_rom_decode.parallel < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %u kB is bigger than supported "
+ "size %u kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode.parallel / 1024, "Parallel");
+ }
+ if ((buses & BUS_LPC) && (max_rom_decode.lpc < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %u kB is bigger than supported "
+ "size %u kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode.lpc / 1024, "LPC");
+ }
+ if ((buses & BUS_FWH) && (max_rom_decode.fwh < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %u kB is bigger than supported "
+ "size %u kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode.fwh / 1024, "FWH");
+ }
+ if ((buses & BUS_SPI) && (max_rom_decode.spi < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %u kB is bigger than supported "
+ "size %u kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode.spi / 1024, "SPI");
+ }
+ return limitexceeded;
+}
+
+void unmap_flash(struct flashctx *flash)
+{
+ if (flash->virtual_registers != (chipaddr)ERROR_PTR) {
+ programmer_unmap_flash_region((void *)flash->virtual_registers, flash->chip->total_size * 1024);
+ flash->physical_registers = 0;
+ flash->virtual_registers = (chipaddr)ERROR_PTR;
+ }
+
+ if (flash->virtual_memory != (chipaddr)ERROR_PTR) {
+ programmer_unmap_flash_region((void *)flash->virtual_memory, flash->chip->total_size * 1024);
+ flash->physical_memory = 0;
+ flash->virtual_memory = (chipaddr)ERROR_PTR;
+ }
+}
+
+int map_flash(struct flashctx *flash)
+{
+ /* Init pointers to the fail-safe state to distinguish them later from legit values. */
+ flash->virtual_memory = (chipaddr)ERROR_PTR;
+ flash->virtual_registers = (chipaddr)ERROR_PTR;
+
+ /* FIXME: This avoids mapping (and unmapping) of flash chip definitions with size 0.
+ * These are used for various probing-related hacks that would not map successfully anyway and should be
+ * removed ASAP. */
+ if (flash->chip->total_size == 0)
+ return 0;
+
+ const chipsize_t size = flash->chip->total_size * 1024;
+ uintptr_t base = flashbase ? flashbase : (0xffffffff - size + 1);
+ void *addr = programmer_map_flash_region(flash->chip->name, base, size);
+ if (addr == ERROR_PTR) {
+ msg_perr("Could not map flash chip %s at 0x%0*" PRIxPTR ".\n",
+ flash->chip->name, PRIxPTR_WIDTH, base);
+ return 1;
+ }
+ flash->physical_memory = base;
+ flash->virtual_memory = (chipaddr)addr;
+
+ /* FIXME: Special function registers normally live 4 MByte below flash space, but it might be somewhere
+ * completely different on some chips and programmers, or not mappable at all.
+ * Ignore these problems for now and always report success. */
+ if (flash->chip->feature_bits & FEATURE_REGISTERMAP) {
+ base = 0xffffffff - size - 0x400000 + 1;
+ addr = programmer_map_flash_region("flash chip registers", base, size);
+ if (addr == ERROR_PTR) {
+ msg_pdbg2("Could not map flash chip registers %s at 0x%0*" PRIxPTR ".\n",
+ flash->chip->name, PRIxPTR_WIDTH, base);
+ return 0;
+ }
+ flash->physical_registers = base;
+ flash->virtual_registers = (chipaddr)addr;
+ }
+ return 0;
+}
+
+int probe_flash(struct registered_master *mst, int startchip, struct flashctx *flash, int force)
+{
+ const struct flashchip *chip;
+ enum chipbustype buses_common;
+ char *tmp;
+
+ for (chip = flashchips + startchip; chip && chip->name; chip++) {
+ if (chip_to_probe && strcmp(chip->name, chip_to_probe) != 0)
+ continue;
+ buses_common = mst->buses_supported & chip->bustype;
+ if (!buses_common)
+ continue;
+ msg_gdbg("Probing for %s %s, %d kB: ", chip->vendor, chip->name, chip->total_size);
+ if (!chip->probe && !force) {
+ msg_gdbg("failed! flashrom has no probe function for this flash chip.\n");
+ continue;
+ }
+
+ /* Start filling in the dynamic data. */
+ flash->chip = calloc(1, sizeof(struct flashchip));
+ if (!flash->chip) {
+ msg_gerr("Out of memory!\n");
+ exit(1);
+ }
+ memcpy(flash->chip, chip, sizeof(struct flashchip));
+ flash->mst = mst;
+
+ if (map_flash(flash) != 0)
+ return -1;
+
+ /* We handle a forced match like a real match, we just avoid probing. Note that probe_flash()
+ * is only called with force=1 after normal probing failed.
+ */
+ if (force)
+ break;
+
+ if (flash->chip->probe(flash) != 1)
+ goto notfound;
+
+ /* If this is the first chip found, accept it.
+ * If this is not the first chip found, accept it only if it is
+ * a non-generic match. SFDP and CFI are generic matches.
+ * startchip==0 means this call to probe_flash() is the first
+ * one for this programmer interface (master) and thus no other chip has
+ * been found on this interface.
+ */
+ if (startchip == 0 && flash->chip->model_id == SFDP_DEVICE_ID) {
+ msg_cinfo("===\n"
+ "SFDP has autodetected a flash chip which is "
+ "not natively supported by flashrom yet.\n");
+ if (count_usable_erasers(flash) == 0)
+ msg_cinfo("The standard operations read and "
+ "verify should work, but to support "
+ "erase, write and all other "
+ "possible features");
+ else
+ msg_cinfo("All standard operations (read, "
+ "verify, erase and write) should "
+ "work, but to support all possible "
+ "features");
+
+ msg_cinfo(" we need to add them manually.\n"
+ "You can help us by mailing us the output of the following command to "
+ "flashrom@flashrom.org:\n"
+ "'flashrom -VV [plus the -p/--programmer parameter]'\n"
+ "Thanks for your help!\n"
+ "===\n");
+ }
+
+ /* First flash chip detected on this bus. */
+ if (startchip == 0)
+ break;
+ /* Not the first flash chip detected on this bus, but not a generic match either. */
+ if ((flash->chip->model_id != GENERIC_DEVICE_ID) && (flash->chip->model_id != SFDP_DEVICE_ID))
+ break;
+ /* Not the first flash chip detected on this bus, and it's just a generic match. Ignore it. */
+notfound:
+ unmap_flash(flash);
+ free(flash->chip);
+ flash->chip = NULL;
+ }
+
+ if (!flash->chip)
+ return -1;
+
+
+ tmp = flashbuses_to_text(flash->chip->bustype);
+ msg_cinfo("%s %s flash chip \"%s\" (%d kB, %s) ", force ? "Assuming" : "Found",
+ flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp);
+ free(tmp);
+#if CONFIG_INTERNAL == 1
+ if (programmer_table[programmer].map_flash_region == physmap)
+ msg_cinfo("mapped at physical address 0x%0*" PRIxPTR ".\n",
+ PRIxPTR_WIDTH, flash->physical_memory);
+ else
+#endif
+ msg_cinfo("on %s.\n", programmer_table[programmer].name);
+
+ /* Flash registers may more likely not be mapped if the chip was forced.
+ * Lock info may be stored in registers, so avoid lock info printing. */
+ if (!force)
+ if (flash->chip->printlock)
+ flash->chip->printlock(flash);
+
+ /* Get out of the way for later runs. */
+ unmap_flash(flash);
+
+ /* Return position of matching chip. */
+ return chip - flashchips;
+}
+
+int read_buf_from_file(unsigned char *buf, unsigned long size,
+ const char *filename)
+{
+#ifdef __LIBPAYLOAD__
+ msg_gerr("Error: No file I/O support in libpayload\n");
+ return 1;
+#else
+ int ret = 0;
+
+ FILE *image;
+ if ((image = fopen(filename, "rb")) == NULL) {
+ msg_gerr("Error: opening file \"%s\" failed: %s\n", filename, strerror(errno));
+ return 1;
+ }
+
+ struct stat image_stat;
+ if (fstat(fileno(image), &image_stat) != 0) {
+ msg_gerr("Error: getting metadata of file \"%s\" failed: %s\n", filename, strerror(errno));
+ ret = 1;
+ goto out;
+ }
+ if (image_stat.st_size != size) {
+ msg_gerr("Error: Image size (%jd B) doesn't match the flash chip's size (%lu B)!\n",
+ (intmax_t)image_stat.st_size, size);
+ ret = 1;
+ goto out;
+ }
+
+ unsigned long numbytes = fread(buf, 1, size, image);
+ if (numbytes != size) {
+ msg_gerr("Error: Failed to read complete file. Got %ld bytes, "
+ "wanted %ld!\n", numbytes, size);
+ ret = 1;
+ }
+out:
+ (void)fclose(image);
+ return ret;
+#endif
+}
+
+int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename)
+{
+#ifdef __LIBPAYLOAD__
+ msg_gerr("Error: No file I/O support in libpayload\n");
+ return 1;
+#else
+ FILE *image;
+ int ret = 0;
+
+ if (!filename) {
+ msg_gerr("No filename specified.\n");
+ return 1;
+ }
+ if ((image = fopen(filename, "wb")) == NULL) {
+ msg_gerr("Error: opening file \"%s\" failed: %s\n", filename, strerror(errno));
+ return 1;
+ }
+
+ unsigned long numbytes = fwrite(buf, 1, size, image);
+ if (numbytes != size) {
+ msg_gerr("Error: file %s could not be written completely.\n", filename);
+ ret = 1;
+ goto out;
+ }
+ if (fflush(image)) {
+ msg_gerr("Error: flushing file \"%s\" failed: %s\n", filename, strerror(errno));
+ ret = 1;
+ }
+ // Try to fsync() only regular files and if that function is available at all (e.g. not on MinGW).
+#if defined(_POSIX_FSYNC) && (_POSIX_FSYNC != -1)
+ struct stat image_stat;
+ if (fstat(fileno(image), &image_stat) != 0) {
+ msg_gerr("Error: getting metadata of file \"%s\" failed: %s\n", filename, strerror(errno));
+ ret = 1;
+ goto out;
+ }
+ if (S_ISREG(image_stat.st_mode)) {
+ if (fsync(fileno(image))) {
+ msg_gerr("Error: fsyncing file \"%s\" failed: %s\n", filename, strerror(errno));
+ ret = 1;
+ }
+ }
+#endif
+out:
+ if (fclose(image)) {
+ msg_gerr("Error: closing file \"%s\" failed: %s\n", filename, strerror(errno));
+ ret = 1;
+ }
+ return ret;
+#endif
+}
+
+int read_flash_to_file(struct flashctx *flash, const char *filename)
+{
+ unsigned long size = flash->chip->total_size * 1024;
+ unsigned char *buf = calloc(size, sizeof(char));
+ int ret = 0;
+
+ msg_cinfo("Reading flash... ");
+ if (!buf) {
+ msg_gerr("Memory allocation failed!\n");
+ msg_cinfo("FAILED.\n");
+ return 1;
+ }
+ if (!flash->chip->read) {
+ msg_cerr("No read function available for this flash chip.\n");
+ ret = 1;
+ goto out_free;
+ }
+ if (flash->chip->read(flash, buf, 0, size)) {
+ msg_cerr("Read operation failed!\n");
+ ret = 1;
+ goto out_free;
+ }
+
+ ret = write_buf_to_file(buf, size, filename);
+out_free:
+ free(buf);
+ msg_cinfo("%s.\n", ret ? "FAILED" : "done");
+ return ret;
+}
+
+/* Even if an error is found, the function will keep going and check the rest. */
+static int selfcheck_eraseblocks(const struct flashchip *chip)
+{
+ int i, j, k;
+ int ret = 0;
+
+ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+ unsigned int done = 0;
+ struct block_eraser eraser = chip->block_erasers[k];
+
+ for (i = 0; i < NUM_ERASEREGIONS; i++) {
+ /* Blocks with zero size are bugs in flashchips.c. */
+ if (eraser.eraseblocks[i].count &&
+ !eraser.eraseblocks[i].size) {
+ msg_gerr("ERROR: Flash chip %s erase function "
+ "%i region %i has size 0. Please report"
+ " a bug at flashrom@flashrom.org\n",
+ chip->name, k, i);
+ ret = 1;
+ }
+ /* Blocks with zero count are bugs in flashchips.c. */
+ if (!eraser.eraseblocks[i].count &&
+ eraser.eraseblocks[i].size) {
+ msg_gerr("ERROR: Flash chip %s erase function "
+ "%i region %i has count 0. Please report"
+ " a bug at flashrom@flashrom.org\n",
+ chip->name, k, i);
+ ret = 1;
+ }
+ done += eraser.eraseblocks[i].count *
+ eraser.eraseblocks[i].size;
+ }
+ /* Empty eraseblock definition with erase function. */
+ if (!done && eraser.block_erase)
+ msg_gspew("Strange: Empty eraseblock definition with "
+ "non-empty erase function. Not an error.\n");
+ if (!done)
+ continue;
+ if (done != chip->total_size * 1024) {
+ msg_gerr("ERROR: Flash chip %s erase function %i "
+ "region walking resulted in 0x%06x bytes total,"
+ " expected 0x%06x bytes. Please report a bug at"
+ " flashrom@flashrom.org\n", chip->name, k,
+ done, chip->total_size * 1024);
+ ret = 1;
+ }
+ if (!eraser.block_erase)
+ continue;
+ /* Check if there are identical erase functions for different
+ * layouts. That would imply "magic" erase functions. The
+ * easiest way to check this is with function pointers.
+ */
+ for (j = k + 1; j < NUM_ERASEFUNCTIONS; j++) {
+ if (eraser.block_erase ==
+ chip->block_erasers[j].block_erase) {
+ msg_gerr("ERROR: Flash chip %s erase function "
+ "%i and %i are identical. Please report"
+ " a bug at flashrom@flashrom.org\n",
+ chip->name, k, j);
+ ret = 1;
+ }
+ }
+ }
+ return ret;
+}
+
+static int erase_and_write_block_helper(struct flashctx *flash,
+ unsigned int start, unsigned int len,
+ uint8_t *curcontents,
+ uint8_t *newcontents,
+ int (*erasefn) (struct flashctx *flash,
+ unsigned int addr,
+ unsigned int len))
+{
+ unsigned int starthere = 0, lenhere = 0;
+ int ret = 0, skip = 1, writecount = 0;
+ enum write_granularity gran = flash->chip->gran;
+
+ /* curcontents and newcontents are opaque to walk_eraseregions, and
+ * need to be adjusted here to keep the impression of proper abstraction
+ */
+ curcontents += start;
+ newcontents += start;
+ msg_cdbg(":");
+ if (need_erase(curcontents, newcontents, len, gran)) {
+ msg_cdbg("E");
+ ret = erasefn(flash, start, len);
+ if (ret)
+ return ret;
+ if (check_erased_range(flash, start, len)) {
+ msg_cerr("ERASE FAILED!\n");
+ return -1;
+ }
+ /* Erase was successful. Adjust curcontents. */
+ memset(curcontents, 0xff, len);
+ skip = 0;
+ }
+ /* get_next_write() sets starthere to a new value after the call. */
+ while ((lenhere = get_next_write(curcontents + starthere,
+ newcontents + starthere,
+ len - starthere, &starthere, gran))) {
+ if (!writecount++)
+ msg_cdbg("W");
+ /* Needs the partial write function signature. */
+ ret = flash->chip->write(flash, newcontents + starthere,
+ start + starthere, lenhere);
+ if (ret)
+ return ret;
+ starthere += lenhere;
+ skip = 0;
+ }
+ if (skip)
+ msg_cdbg("S");
+ else
+ all_skipped = false;
+ return ret;
+}
+
+static int walk_eraseregions(struct flashctx *flash, int erasefunction,
+ int (*do_something) (struct flashctx *flash,
+ unsigned int addr,
+ unsigned int len,
+ uint8_t *param1,
+ uint8_t *param2,
+ int (*erasefn) (
+ struct flashctx *flash,
+ unsigned int addr,
+ unsigned int len)),
+ void *param1, void *param2)
+{
+ int i, j;
+ unsigned int start = 0;
+ unsigned int len;
+ struct block_eraser eraser = flash->chip->block_erasers[erasefunction];
+
+ for (i = 0; i < NUM_ERASEREGIONS; i++) {
+ /* count==0 for all automatically initialized array
+ * members so the loop below won't be executed for them.
+ */
+ len = eraser.eraseblocks[i].size;
+ for (j = 0; j < eraser.eraseblocks[i].count; j++) {
+ /* Print this for every block except the first one. */
+ if (i || j)
+ msg_cdbg(", ");
+ msg_cdbg("0x%06x-0x%06x", start,
+ start + len - 1);
+ if (do_something(flash, start, len, param1, param2,
+ eraser.block_erase)) {
+ return 1;
+ }
+ start += len;
+ }
+ }
+ msg_cdbg("\n");
+ return 0;
+}
+
+static int check_block_eraser(const struct flashctx *flash, int k, int log)
+{
+ struct block_eraser eraser = flash->chip->block_erasers[k];
+
+ if (!eraser.block_erase && !eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("not defined. ");
+ return 1;
+ }
+ if (!eraser.block_erase && eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("eraseblock layout is known, but matching "
+ "block erase function is not implemented. ");
+ return 1;
+ }
+ if (eraser.block_erase && !eraser.eraseblocks[0].count) {
+ if (log)
+ msg_cdbg("block erase function found, but "
+ "eraseblock layout is not defined. ");
+ return 1;
+ }
+ // TODO: Once erase functions are annotated with allowed buses, check that as well.
+ return 0;
+}
+
+int erase_and_write_flash(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents)
+{
+ int k, ret = 1;
+ uint8_t *curcontents;
+ unsigned long size = flash->chip->total_size * 1024;
+ unsigned int usable_erasefunctions = count_usable_erasers(flash);
+
+ msg_cinfo("Erasing and writing flash chip... ");
+ curcontents = malloc(size);
+ if (!curcontents) {
+ msg_gerr("Out of memory!\n");
+ exit(1);
+ }
+ /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */
+ memcpy(curcontents, oldcontents, size);
+
+ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+ if (k != 0)
+ msg_cinfo("Looking for another erase function.\n");
+ if (!usable_erasefunctions) {
+ msg_cinfo("No usable erase functions left.\n");
+ break;
+ }
+ msg_cdbg("Trying erase function %i... ", k);
+ if (check_block_eraser(flash, k, 1))
+ continue;
+ usable_erasefunctions--;
+ ret = walk_eraseregions(flash, k, &erase_and_write_block_helper,
+ curcontents, newcontents);
+ /* If everything is OK, don't try another erase function. */
+ if (!ret)
+ break;
+ /* Write/erase failed, so try to find out what the current chip
+ * contents are. If no usable erase functions remain, we can
+ * skip this: the next iteration will break immediately anyway.
+ */
+ if (!usable_erasefunctions)
+ continue;
+ /* Reading the whole chip may take a while, inform the user even
+ * in non-verbose mode.
+ */
+ msg_cinfo("Reading current flash chip contents... ");
+ if (flash->chip->read(flash, curcontents, 0, size)) {
+ /* Now we are truly screwed. Read failed as well. */
+ msg_cerr("Can't read anymore! Aborting.\n");
+ /* We have no idea about the flash chip contents, so
+ * retrying with another erase function is pointless.
+ */
+ break;
+ }
+ msg_cinfo("done. ");
+ }
+ /* Free the scratchpad. */
+ free(curcontents);
+
+ if (ret) {
+ msg_cerr("FAILED!\n");
+ } else {
+ if (all_skipped)
+ msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
+ msg_cinfo("Erase/write done.\n");
+ }
+ return ret;
+}
+
+static void nonfatal_help_message(void)
+{
+ msg_gerr("Good, writing to the flash chip apparently didn't do anything.\n");
+#if CONFIG_INTERNAL == 1
+ if (programmer == PROGRAMMER_INTERNAL)
+ msg_gerr("This means we have to add special support for your board, programmer or flash\n"
+ "chip. Please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
+ "mail flashrom@flashrom.org, thanks!\n"
+ "-------------------------------------------------------------------------------\n"
+ "You may now reboot or simply leave the machine running.\n");
+ else
+#endif
+ msg_gerr("Please check the connections (especially those to write protection pins) between\n"
+ "the programmer and the flash chip. If you think the error is caused by flashrom\n"
+ "please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
+ "mail flashrom@flashrom.org, thanks!\n");
+}
+
+static void emergency_help_message(void)
+{
+ msg_gerr("Your flash chip is in an unknown state.\n");
+#if CONFIG_INTERNAL == 1
+ if (programmer == PROGRAMMER_INTERNAL)
+ msg_gerr("Get help on IRC at chat.freenode.net (channel #flashrom) or\n"
+ "mail flashrom@flashrom.org with the subject \"FAILED: <your board name>\"!\n"
+ "-------------------------------------------------------------------------------\n"
+ "DO NOT REBOOT OR POWEROFF!\n");
+ else
+#endif
+ msg_gerr("Please report this on IRC at chat.freenode.net (channel #flashrom) or\n"
+ "mail flashrom@flashrom.org, thanks!\n");
+}
+
+/* The way to go if you want a delimited list of programmers */
+void list_programmers(const char *delim)
+{
+ enum programmer p;
+ for (p = 0; p < PROGRAMMER_INVALID; p++) {
+ msg_ginfo("%s", programmer_table[p].name);
+ if (p < PROGRAMMER_INVALID - 1)
+ msg_ginfo("%s", delim);
+ }
+ msg_ginfo("\n");
+}
+
+void list_programmers_linebreak(int startcol, int cols, int paren)
+{
+ const char *pname;
+ int pnamelen;
+ int remaining = 0, firstline = 1;
+ enum programmer p;
+ int i;
+
+ for (p = 0; p < PROGRAMMER_INVALID; p++) {
+ pname = programmer_table[p].name;
+ pnamelen = strlen(pname);
+ if (remaining - pnamelen - 2 < 0) {
+ if (firstline)
+ firstline = 0;
+ else
+ msg_ginfo("\n");
+ for (i = 0; i < startcol; i++)
+ msg_ginfo(" ");
+ remaining = cols - startcol;
+ } else {
+ msg_ginfo(" ");
+ remaining--;
+ }
+ if (paren && (p == 0)) {
+ msg_ginfo("(");
+ remaining--;
+ }
+ msg_ginfo("%s", pname);
+ remaining -= pnamelen;
+ if (p < PROGRAMMER_INVALID - 1) {
+ msg_ginfo(",");
+ remaining--;
+ } else {
+ if (paren)
+ msg_ginfo(")");
+ }
+ }
+}
+
+void print_sysinfo(void)
+{
+#if IS_WINDOWS
+ SYSTEM_INFO si;
+ OSVERSIONINFOEX osvi;
+
+ memset(&si, 0, sizeof(SYSTEM_INFO));
+ memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
+ msg_ginfo(" on Windows");
+ /* Tell Windows which version of the structure we want. */
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if (GetVersionEx((OSVERSIONINFO*) &osvi))
+ msg_ginfo(" %lu.%lu", osvi.dwMajorVersion, osvi.dwMinorVersion);
+ else
+ msg_ginfo(" unknown version");
+ GetSystemInfo(&si);
+ switch (si.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ msg_ginfo(" (x86_64)");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ msg_ginfo(" (x86)");
+ break;
+ default:
+ msg_ginfo(" (unknown arch)");
+ break;
+ }
+#elif HAVE_UTSNAME == 1
+ struct utsname osinfo;
+
+ uname(&osinfo);
+ msg_ginfo(" on %s %s (%s)", osinfo.sysname, osinfo.release,
+ osinfo.machine);
+#else
+ msg_ginfo(" on unknown machine");
+#endif
+}
+
+void print_buildinfo(void)
+{
+ msg_gdbg("flashrom was built with");
+#if NEED_PCI == 1
+#ifdef PCILIB_VERSION
+ msg_gdbg(" libpci %s,", PCILIB_VERSION);
+#else
+ msg_gdbg(" unknown PCI library,");
+#endif
+#endif
+#ifdef __clang__
+ msg_gdbg(" LLVM Clang");
+#ifdef __clang_version__
+ msg_gdbg(" %s,", __clang_version__);
+#else
+ msg_gdbg(" unknown version (before r102686),");
+#endif
+#elif defined(__GNUC__)
+ msg_gdbg(" GCC");
+#ifdef __VERSION__
+ msg_gdbg(" %s,", __VERSION__);
+#else
+ msg_gdbg(" unknown version,");
+#endif
+#else
+ msg_gdbg(" unknown compiler,");
+#endif
+#if defined (__FLASHROM_LITTLE_ENDIAN__)
+ msg_gdbg(" little endian");
+#elif defined (__FLASHROM_BIG_ENDIAN__)
+ msg_gdbg(" big endian");
+#else
+#error Endianness could not be determined
+#endif
+ msg_gdbg("\n");
+}
+
+void print_version(void)
+{
+ msg_ginfo("flashrom v%s", flashrom_version);
+ print_sysinfo();
+ msg_ginfo("\n");
+}
+
+void print_banner(void)
+{
+ msg_ginfo("flashrom is free software, get the source code at "
+ "https://flashrom.org\n");
+ msg_ginfo("\n");
+}
+
+int selfcheck(void)
+{
+ unsigned int i;
+ int ret = 0;
+
+ /* Safety check. Instead of aborting after the first error, check
+ * if more errors exist.
+ */
+ if (ARRAY_SIZE(programmer_table) - 1 != PROGRAMMER_INVALID) {
+ msg_gerr("Programmer table miscompilation!\n");
+ ret = 1;
+ }
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ const struct programmer_entry p = programmer_table[i];
+ if (p.name == NULL) {
+ msg_gerr("All programmers need a valid name, but the one with index %d does not!\n", i);
+ ret = 1;
+ /* This might hide other problems with this programmer, but allows for better error
+ * messages below without jumping through hoops. */
+ continue;
+ }
+ switch (p.type) {
+ case USB:
+ case PCI:
+ case OTHER:
+ if (p.devs.note == NULL) {
+ if (strcmp("internal", p.name) == 0)
+ break; /* This one has its device list stored separately. */
+ msg_gerr("Programmer %s has neither a device list nor a textual description!\n",
+ p.name);
+ ret = 1;
+ }
+ break;
+ default:
+ msg_gerr("Programmer %s does not have a valid type set!\n", p.name);
+ ret = 1;
+ break;
+ }
+ if (p.init == NULL) {
+ msg_gerr("Programmer %s does not have a valid init function!\n", p.name);
+ ret = 1;
+ }
+ if (p.delay == NULL) {
+ msg_gerr("Programmer %s does not have a valid delay function!\n", p.name);
+ ret = 1;
+ }
+ if (p.map_flash_region == NULL) {
+ msg_gerr("Programmer %s does not have a valid map_flash_region function!\n", p.name);
+ ret = 1;
+ }
+ if (p.unmap_flash_region == NULL) {
+ msg_gerr("Programmer %s does not have a valid unmap_flash_region function!\n", p.name);
+ ret = 1;
+ }
+ }
+
+ /* It would be favorable if we could check for the correct layout (especially termination) of various
+ * constant arrays: flashchips, chipset_enables, board_matches, boards_known, laptops_known.
+ * They are all defined as externs in this compilation unit so we don't know their sizes which vary
+ * depending on compiler flags, e.g. the target architecture, and can sometimes be 0.
+ * For 'flashchips' we export the size explicitly to work around this and to be able to implement the
+ * checks below. */
+ if (flashchips_size <= 1 || flashchips[flashchips_size - 1].name != NULL) {
+ msg_gerr("Flashchips table miscompilation!\n");
+ ret = 1;
+ } else {
+ for (i = 0; i < flashchips_size - 1; i++) {
+ const struct flashchip *chip = &flashchips[i];
+ if (chip->vendor == NULL || chip->name == NULL || chip->bustype == BUS_NONE) {
+ ret = 1;
+ msg_gerr("ERROR: Some field of flash chip #%d (%s) is misconfigured.\n"
+ "Please report a bug at flashrom@flashrom.org\n", i,
+ chip->name == NULL ? "unnamed" : chip->name);
+ }
+ if (selfcheck_eraseblocks(chip)) {
+ ret = 1;
+ }
+ }
+ }
+
+#if CONFIG_INTERNAL == 1
+ ret |= selfcheck_board_enables();
+#endif
+
+ /* TODO: implement similar sanity checks for other arrays where deemed necessary. */
+ return ret;
+}
+
+/* FIXME: This function signature needs to be improved once doit() has a better
+ * function signature.
+ */
+int chip_safety_check(const struct flashctx *flash, int force, int read_it, int write_it, int erase_it,
+ int verify_it)
+{
+ const struct flashchip *chip = flash->chip;
+
+ if (!programmer_may_write && (write_it || erase_it)) {
+ msg_perr("Write/erase is not working yet on your programmer in "
+ "its current configuration.\n");
+ /* --force is the wrong approach, but it's the best we can do
+ * until the generic programmer parameter parser is merged.
+ */
+ if (!force)
+ return 1;
+ msg_cerr("Continuing anyway.\n");
+ }
+
+ if (read_it || erase_it || write_it || verify_it) {
+ /* Everything needs read. */
+ if (chip->tested.read == BAD) {
+ msg_cerr("Read is not working on this chip. ");
+ if (!force)
+ return 1;
+ msg_cerr("Continuing anyway.\n");
+ }
+ if (!chip->read) {
+ msg_cerr("flashrom has no read function for this "
+ "flash chip.\n");
+ return 1;
+ }
+ }
+ if (erase_it || write_it) {
+ /* Write needs erase. */
+ if (chip->tested.erase == NA) {
+ msg_cerr("Erase is not possible on this chip.\n");
+ return 1;
+ }
+ if (chip->tested.erase == BAD) {
+ msg_cerr("Erase is not working on this chip. ");
+ if (!force)
+ return 1;
+ msg_cerr("Continuing anyway.\n");
+ }
+ if(count_usable_erasers(flash) == 0) {
+ msg_cerr("flashrom has no erase function for this "
+ "flash chip.\n");
+ return 1;
+ }
+ }
+ if (write_it) {
+ if (chip->tested.write == NA) {
+ msg_cerr("Write is not possible on this chip.\n");
+ return 1;
+ }
+ if (chip->tested.write == BAD) {
+ msg_cerr("Write is not working on this chip. ");
+ if (!force)
+ return 1;
+ msg_cerr("Continuing anyway.\n");
+ }
+ if (!chip->write) {
+ msg_cerr("flashrom has no write function for this "
+ "flash chip.\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* This function signature is horrible. We need to design a better interface,
+ * but right now it allows us to split off the CLI code.
+ * Besides that, the function itself is a textbook example of abysmal code flow.
+ */
+int doit(struct flashctx *flash, int force, const char *filename, int read_it,
+ int write_it, int erase_it, int verify_it)
+{
+ uint8_t *oldcontents;
+ uint8_t *newcontents;
+ int ret = 0;
+ unsigned long size = flash->chip->total_size * 1024;
+ int read_all_first = 1; /* FIXME: Make this configurable. */
+
+ if (chip_safety_check(flash, force, read_it, write_it, erase_it, verify_it)) {
+ msg_cerr("Aborting.\n");
+ return 1;
+ }
+
+ if (normalize_romentries(flash)) {
+ msg_cerr("Requested regions can not be handled. Aborting.\n");
+ return 1;
+ }
+
+ /* Given the existence of read locks, we want to unlock for read,
+ * erase and write.
+ */
+ if (flash->chip->unlock)
+ flash->chip->unlock(flash);
+
+ if (read_it) {
+ return read_flash_to_file(flash, filename);
+ }
+
+ oldcontents = malloc(size);
+ if (!oldcontents) {
+ msg_gerr("Out of memory!\n");
+ exit(1);
+ }
+ /* Assume worst case: All bits are 0. */
+ memset(oldcontents, 0x00, size);
+ newcontents = malloc(size);
+ if (!newcontents) {
+ msg_gerr("Out of memory!\n");
+ exit(1);
+ }
+ /* Assume best case: All bits should be 1. */
+ memset(newcontents, 0xff, size);
+ /* Side effect of the assumptions above: Default write action is erase
+ * because newcontents looks like a completely erased chip, and
+ * oldcontents being completely 0x00 means we have to erase everything
+ * before we can write.
+ */
+
+ if (erase_it) {
+ /* FIXME: Do we really want the scary warning if erase failed?
+ * After all, after erase the chip is either blank or partially
+ * blank or it has the old contents. A blank chip won't boot,
+ * so if the user wanted erase and reboots afterwards, the user
+ * knows very well that booting won't work.
+ */
+ if (erase_and_write_flash(flash, oldcontents, newcontents)) {
+ emergency_help_message();
+ ret = 1;
+ }
+ goto out;
+ }
+
+ if (write_it || verify_it) {
+ if (read_buf_from_file(newcontents, size, filename)) {
+ ret = 1;
+ goto out;
+ }
+
+#if CONFIG_INTERNAL == 1
+ if (programmer == PROGRAMMER_INTERNAL && cb_check_image(newcontents, size) < 0) {
+ if (force_boardmismatch) {
+ msg_pinfo("Proceeding anyway because user forced us to.\n");
+ } else {
+ msg_perr("Aborting. You can override this with "
+ "-p internal:boardmismatch=force.\n");
+ ret = 1;
+ goto out;
+ }
+ }
+#endif
+ }
+
+ /* Read the whole chip to be able to check whether regions need to be
+ * erased and to give better diagnostics in case write fails.
+ * The alternative is to read only the regions which are to be
+ * preserved, but in that case we might perform unneeded erase which
+ * takes time as well.
+ */
+ if (read_all_first) {
+ msg_cinfo("Reading old flash chip contents... ");
+ if (flash->chip->read(flash, oldcontents, 0, size)) {
+ ret = 1;
+ msg_cinfo("FAILED.\n");
+ goto out;
+ }
+ }
+ msg_cinfo("done.\n");
+
+ /* Build a new image taking the given layout into account. */
+ if (build_new_image(flash, read_all_first, oldcontents, newcontents)) {
+ msg_gerr("Could not prepare the data to be written, aborting.\n");
+ ret = 1;
+ goto out;
+ }
+
+ // ////////////////////////////////////////////////////////////
+
+ if (write_it && erase_and_write_flash(flash, oldcontents, newcontents)) {
+ msg_cerr("Uh oh. Erase/write failed. ");
+ if (read_all_first) {
+ msg_cerr("Checking if anything has changed.\n");
+ msg_cinfo("Reading current flash chip contents... ");
+ if (!flash->chip->read(flash, newcontents, 0, size)) {
+ msg_cinfo("done.\n");
+ if (!memcmp(oldcontents, newcontents, size)) {
+ nonfatal_help_message();
+ ret = 1;
+ goto out;
+ }
+ msg_cerr("Apparently at least some data has changed.\n");
+ } else
+ msg_cerr("Can't even read anymore!\n");
+ emergency_help_message();
+ ret = 1;
+ goto out;
+ } else
+ msg_cerr("\n");
+ emergency_help_message();
+ ret = 1;
+ goto out;
+ }
+
+ /* Verify only if we either did not try to write (verify operation) or actually changed something. */
+ if (verify_it && (!write_it || !all_skipped)) {
+ msg_cinfo("Verifying flash... ");
+
+ if (write_it) {
+ /* Work around chips which need some time to calm down. */
+ programmer_delay(1000*1000);
+ ret = verify_range(flash, newcontents, 0, size);
+ /* If we tried to write, and verification now fails, we
+ * might have an emergency situation.
+ */
+ if (ret)
+ emergency_help_message();
+ } else {
+ ret = compare_range(newcontents, oldcontents, 0, size);
+ }
+ if (!ret)
+ msg_cinfo("VERIFIED.\n");
+ }
+
+out:
+ free(oldcontents);
+ free(newcontents);
+ return ret;
+}
diff --git a/ft2232_spi.c b/ft2232_spi.c
new file mode 100644
index 0000000..be60837
--- /dev/null
+++ b/ft2232_spi.c
@@ -0,0 +1,520 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Paul Fox <pgf@laptop.org>
+ * Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if CONFIG_FT2232_SPI == 1
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+#include <ftdi.h>
+
+/* This is not defined in libftdi.h <0.20 (c7e4c09e68cfa6f5e112334aa1b3bb23401c8dc7 to be exact).
+ * Some tests indicate that his is the only change that it is needed to support the FT232H in flashrom. */
+#if !defined(HAVE_FT232H)
+#define TYPE_232H 6
+#endif
+
+/* Please keep sorted by vendor ID, then device ID. */
+
+#define FTDI_VID 0x0403
+#define FTDI_FT2232H_PID 0x6010
+#define FTDI_FT4232H_PID 0x6011
+#define FTDI_FT232H_PID 0x6014
+#define TIAO_TUMPA_PID 0x8a98
+#define TIAO_TUMPA_LITE_PID 0x8a99
+#define AMONTEC_JTAGKEY_PID 0xCFF8
+
+#define GOEPEL_VID 0x096C
+#define GOEPEL_PICOTAP_PID 0x1449
+
+#define FIC_VID 0x1457
+#define OPENMOKO_DBGBOARD_PID 0x5118
+
+#define OLIMEX_VID 0x15BA
+#define OLIMEX_ARM_OCD_PID 0x0003
+#define OLIMEX_ARM_TINY_PID 0x0004
+#define OLIMEX_ARM_OCD_H_PID 0x002B
+#define OLIMEX_ARM_TINY_H_PID 0x002A
+
+#define GOOGLE_VID 0x18D1
+#define GOOGLE_SERVO_PID 0x5001
+#define GOOGLE_SERVO_V2_PID0 0x5002
+#define GOOGLE_SERVO_V2_PID1 0x5003
+
+const struct dev_entry devs_ft2232spi[] = {
+ {FTDI_VID, FTDI_FT2232H_PID, OK, "FTDI", "FT2232H"},
+ {FTDI_VID, FTDI_FT4232H_PID, OK, "FTDI", "FT4232H"},
+ {FTDI_VID, FTDI_FT232H_PID, OK, "FTDI", "FT232H"},
+ {FTDI_VID, TIAO_TUMPA_PID, OK, "TIAO", "USB Multi-Protocol Adapter"},
+ {FTDI_VID, TIAO_TUMPA_LITE_PID, OK, "TIAO", "USB Multi-Protocol Adapter Lite"},
+ {FTDI_VID, AMONTEC_JTAGKEY_PID, OK, "Amontec", "JTAGkey"},
+ {GOEPEL_VID, GOEPEL_PICOTAP_PID, OK, "GOEPEL", "PicoTAP"},
+ {GOOGLE_VID, GOOGLE_SERVO_PID, OK, "Google", "Servo"},
+ {GOOGLE_VID, GOOGLE_SERVO_V2_PID0, OK, "Google", "Servo V2 Legacy"},
+ {GOOGLE_VID, GOOGLE_SERVO_V2_PID1, OK, "Google", "Servo V2"},
+ {FIC_VID, OPENMOKO_DBGBOARD_PID, OK, "FIC", "OpenMoko Neo1973 Debug board (V2+)"},
+ {OLIMEX_VID, OLIMEX_ARM_OCD_PID, OK, "Olimex", "ARM-USB-OCD"},
+ {OLIMEX_VID, OLIMEX_ARM_TINY_PID, OK, "Olimex", "ARM-USB-TINY"},
+ {OLIMEX_VID, OLIMEX_ARM_OCD_H_PID, OK, "Olimex", "ARM-USB-OCD-H"},
+ {OLIMEX_VID, OLIMEX_ARM_TINY_H_PID, OK, "Olimex", "ARM-USB-TINY-H"},
+
+ {0},
+};
+
+#define DEFAULT_DIVISOR 2
+
+#define BITMODE_BITBANG_NORMAL 1
+#define BITMODE_BITBANG_SPI 2
+
+/* The variables cs_bits and pindir store the values for the "set data bits low byte" MPSSE command that
+ * sets the initial state and the direction of the I/O pins. The pin offsets are as follows:
+ * SCK is bit 0.
+ * DO is bit 1.
+ * DI is bit 2.
+ * CS is bit 3.
+ *
+ * The default values (set below) are used for most devices:
+ * value: 0x08 CS=high, DI=low, DO=low, SK=low
+ * dir: 0x0b CS=output, DI=input, DO=output, SK=output
+ */
+static uint8_t cs_bits = 0x08;
+static uint8_t pindir = 0x0b;
+static struct ftdi_context ftdic_context;
+
+static const char *get_ft2232_devicename(int ft2232_vid, int ft2232_type)
+{
+ int i;
+ for (i = 0; devs_ft2232spi[i].vendor_name != NULL; i++) {
+ if ((devs_ft2232spi[i].device_id == ft2232_type) && (devs_ft2232spi[i].vendor_id == ft2232_vid))
+ return devs_ft2232spi[i].device_name;
+ }
+ return "unknown device";
+}
+
+static const char *get_ft2232_vendorname(int ft2232_vid, int ft2232_type)
+{
+ int i;
+ for (i = 0; devs_ft2232spi[i].vendor_name != NULL; i++) {
+ if ((devs_ft2232spi[i].device_id == ft2232_type) && (devs_ft2232spi[i].vendor_id == ft2232_vid))
+ return devs_ft2232spi[i].vendor_name;
+ }
+ return "unknown vendor";
+}
+
+static int send_buf(struct ftdi_context *ftdic, const unsigned char *buf,
+ int size)
+{
+ int r;
+ r = ftdi_write_data(ftdic, (unsigned char *) buf, size);
+ if (r < 0) {
+ msg_perr("ftdi_write_data: %d, %s\n", r, ftdi_get_error_string(ftdic));
+ return 1;
+ }
+ return 0;
+}
+
+static int get_buf(struct ftdi_context *ftdic, const unsigned char *buf,
+ int size)
+{
+ int r;
+
+ while (size > 0) {
+ r = ftdi_read_data(ftdic, (unsigned char *) buf, size);
+ if (r < 0) {
+ msg_perr("ftdi_read_data: %d, %s\n", r, ftdi_get_error_string(ftdic));
+ return 1;
+ }
+ buf += r;
+ size -= r;
+ }
+ return 0;
+}
+
+static int ft2232_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+
+static const struct spi_master spi_master_ft2232 = {
+ .type = SPI_CONTROLLER_FT2232,
+ .max_data_read = 64 * 1024,
+ .max_data_write = 256,
+ .command = ft2232_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+/* Returns 0 upon success, a negative number upon errors. */
+int ft2232_spi_init(void)
+{
+ int ret = 0;
+ struct ftdi_context *ftdic = &ftdic_context;
+ unsigned char buf[512];
+ int ft2232_vid = FTDI_VID;
+ int ft2232_type = FTDI_FT4232H_PID;
+ int channel_count = 4; /* Stores the number of channels of the device. */
+ enum ftdi_interface ft2232_interface = INTERFACE_A;
+ /*
+ * The 'H' chips can run with an internal clock of either 12 MHz or 60 MHz,
+ * but the non-H chips can only run at 12 MHz. We enable the divide-by-5
+ * prescaler on the former to run on the same speed.
+ */
+ uint8_t clock_5x = 1;
+ /* In addition to the prescaler mentioned above there is also another
+ * configurable one on all versions of the chips. Its divisor div can be
+ * set by a 16 bit value x according to the following formula:
+ * div = (1 + x) * 2 <-> x = div / 2 - 1
+ * Hence the expressible divisors are all even numbers between 2 and
+ * 2^17 (=131072) resulting in SCK frequencies of 6 MHz down to about
+ * 92 Hz for 12 MHz inputs.
+ */
+ uint32_t divisor = DEFAULT_DIVISOR;
+ int f;
+ char *arg;
+ double mpsse_clk;
+
+ arg = extract_programmer_param("type");
+ if (arg) {
+ if (!strcasecmp(arg, "2232H")) {
+ ft2232_type = FTDI_FT2232H_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "4232H")) {
+ ft2232_type = FTDI_FT4232H_PID;
+ channel_count = 4;
+ } else if (!strcasecmp(arg, "232H")) {
+ ft2232_type = FTDI_FT232H_PID;
+ channel_count = 1;
+ } else if (!strcasecmp(arg, "jtagkey")) {
+ ft2232_type = AMONTEC_JTAGKEY_PID;
+ channel_count = 2;
+ /* JTAGkey(2) needs to enable its output via Bit4 / GPIOL0
+ * value: 0x18 OE=high, CS=high, DI=low, DO=low, SK=low
+ * dir: 0x1b OE=output, CS=output, DI=input, DO=output, SK=output */
+ cs_bits = 0x18;
+ pindir = 0x1b;
+ } else if (!strcasecmp(arg, "picotap")) {
+ ft2232_vid = GOEPEL_VID;
+ ft2232_type = GOEPEL_PICOTAP_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "tumpa")) {
+ /* Interface A is SPI1, B is SPI2. */
+ ft2232_type = TIAO_TUMPA_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "tumpalite")) {
+ /* Only one channel is used on lite edition */
+ ft2232_type = TIAO_TUMPA_LITE_PID;
+ channel_count = 1;
+ } else if (!strcasecmp(arg, "busblaster")) {
+ /* In its default configuration it is a jtagkey clone */
+ ft2232_type = FTDI_FT2232H_PID;
+ channel_count = 2;
+ cs_bits = 0x18;
+ pindir = 0x1b;
+ } else if (!strcasecmp(arg, "openmoko")) {
+ ft2232_vid = FIC_VID;
+ ft2232_type = OPENMOKO_DBGBOARD_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "arm-usb-ocd")) {
+ ft2232_vid = OLIMEX_VID;
+ ft2232_type = OLIMEX_ARM_OCD_PID;
+ channel_count = 2;
+ /* arm-usb-ocd(-h) has an output buffer that needs to be enabled by pulling ADBUS4 low.
+ * value: 0x08 #OE=low, CS=high, DI=low, DO=low, SK=low
+ * dir: 0x1b #OE=output, CS=output, DI=input, DO=output, SK=output */
+ cs_bits = 0x08;
+ pindir = 0x1b;
+ } else if (!strcasecmp(arg, "arm-usb-tiny")) {
+ ft2232_vid = OLIMEX_VID;
+ ft2232_type = OLIMEX_ARM_TINY_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "arm-usb-ocd-h")) {
+ ft2232_vid = OLIMEX_VID;
+ ft2232_type = OLIMEX_ARM_OCD_H_PID;
+ channel_count = 2;
+ /* See arm-usb-ocd */
+ cs_bits = 0x08;
+ pindir = 0x1b;
+ } else if (!strcasecmp(arg, "arm-usb-tiny-h")) {
+ ft2232_vid = OLIMEX_VID;
+ ft2232_type = OLIMEX_ARM_TINY_H_PID;
+ channel_count = 2;
+ } else if (!strcasecmp(arg, "google-servo")) {
+ ft2232_vid = GOOGLE_VID;
+ ft2232_type = GOOGLE_SERVO_PID;
+ } else if (!strcasecmp(arg, "google-servo-v2")) {
+ ft2232_vid = GOOGLE_VID;
+ ft2232_type = GOOGLE_SERVO_V2_PID1;
+ /* Default divisor is too fast, and chip ID fails */
+ divisor = 6;
+ } else if (!strcasecmp(arg, "google-servo-v2-legacy")) {
+ ft2232_vid = GOOGLE_VID;
+ ft2232_type = GOOGLE_SERVO_V2_PID0;
+ } else {
+ msg_perr("Error: Invalid device type specified.\n");
+ free(arg);
+ return -1;
+ }
+ }
+ free(arg);
+
+ arg = extract_programmer_param("port");
+ if (arg) {
+ switch (toupper((unsigned char)*arg)) {
+ case 'A':
+ ft2232_interface = INTERFACE_A;
+ break;
+ case 'B':
+ ft2232_interface = INTERFACE_B;
+ if (channel_count < 2)
+ channel_count = -1;
+ break;
+ case 'C':
+ ft2232_interface = INTERFACE_C;
+ if (channel_count < 3)
+ channel_count = -1;
+ break;
+ case 'D':
+ ft2232_interface = INTERFACE_D;
+ if (channel_count < 4)
+ channel_count = -1;
+ break;
+ default:
+ channel_count = -1;
+ break;
+ }
+ if (channel_count < 0 || strlen(arg) != 1) {
+ msg_perr("Error: Invalid channel/port/interface specified: \"%s\".\n", arg);
+ free(arg);
+ return -2;
+ }
+ }
+ free(arg);
+
+ arg = extract_programmer_param("divisor");
+ if (arg && strlen(arg)) {
+ unsigned int temp = 0;
+ char *endptr;
+ temp = strtoul(arg, &endptr, 10);
+ if (*endptr || temp < 2 || temp > 131072 || temp & 0x1) {
+ msg_perr("Error: Invalid SPI frequency divisor specified: \"%s\".\n"
+ "Valid are even values between 2 and 131072.\n", arg);
+ free(arg);
+ return -2;
+ } else {
+ divisor = (uint32_t)temp;
+ }
+ }
+ free(arg);
+
+ msg_pdbg("Using device type %s %s ",
+ get_ft2232_vendorname(ft2232_vid, ft2232_type),
+ get_ft2232_devicename(ft2232_vid, ft2232_type));
+ msg_pdbg("channel %s.\n",
+ (ft2232_interface == INTERFACE_A) ? "A" :
+ (ft2232_interface == INTERFACE_B) ? "B" :
+ (ft2232_interface == INTERFACE_C) ? "C" : "D");
+
+ if (ftdi_init(ftdic) < 0) {
+ msg_perr("ftdi_init failed.\n");
+ return -3;
+ }
+
+ if (ftdi_set_interface(ftdic, ft2232_interface) < 0) {
+ msg_perr("Unable to select channel (%s).\n", ftdi_get_error_string(ftdic));
+ }
+
+ arg = extract_programmer_param("serial");
+ f = ftdi_usb_open_desc(ftdic, ft2232_vid, ft2232_type, NULL, arg);
+ free(arg);
+
+ if (f < 0 && f != -5) {
+ msg_perr("Unable to open FTDI device: %d (%s).\n", f, ftdi_get_error_string(ftdic));
+ return -4;
+ }
+
+ if (ftdic->type != TYPE_2232H && ftdic->type != TYPE_4232H && ftdic->type != TYPE_232H) {
+ msg_pdbg("FTDI chip type %d is not high-speed.\n", ftdic->type);
+ clock_5x = 0;
+ }
+
+ if (ftdi_usb_reset(ftdic) < 0) {
+ msg_perr("Unable to reset FTDI device (%s).\n", ftdi_get_error_string(ftdic));
+ }
+
+ if (ftdi_set_latency_timer(ftdic, 2) < 0) {
+ msg_perr("Unable to set latency timer (%s).\n", ftdi_get_error_string(ftdic));
+ }
+
+ if (ftdi_write_data_set_chunksize(ftdic, 256)) {
+ msg_perr("Unable to set chunk size (%s).\n", ftdi_get_error_string(ftdic));
+ }
+
+ if (ftdi_set_bitmode(ftdic, 0x00, BITMODE_BITBANG_SPI) < 0) {
+ msg_perr("Unable to set bitmode to SPI (%s).\n", ftdi_get_error_string(ftdic));
+ }
+
+ if (clock_5x) {
+ msg_pdbg("Disable divide-by-5 front stage\n");
+ buf[0] = 0x8a; /* Disable divide-by-5. DIS_DIV_5 in newer libftdi */
+ if (send_buf(ftdic, buf, 1)) {
+ ret = -5;
+ goto ftdi_err;
+ }
+ mpsse_clk = 60.0;
+ } else {
+ mpsse_clk = 12.0;
+ }
+
+ msg_pdbg("Set clock divisor\n");
+ buf[0] = TCK_DIVISOR;
+ buf[1] = (divisor / 2 - 1) & 0xff;
+ buf[2] = ((divisor / 2 - 1) >> 8) & 0xff;
+ if (send_buf(ftdic, buf, 3)) {
+ ret = -6;
+ goto ftdi_err;
+ }
+
+ msg_pdbg("MPSSE clock: %f MHz, divisor: %u, SPI clock: %f MHz\n",
+ mpsse_clk, divisor, (double)(mpsse_clk / divisor));
+
+ /* Disconnect TDI/DO to TDO/DI for loopback. */
+ msg_pdbg("No loopback of TDI/DO TDO/DI\n");
+ buf[0] = LOOPBACK_END;
+ if (send_buf(ftdic, buf, 1)) {
+ ret = -7;
+ goto ftdi_err;
+ }
+
+ msg_pdbg("Set data bits\n");
+ buf[0] = SET_BITS_LOW;
+ buf[1] = cs_bits;
+ buf[2] = pindir;
+ if (send_buf(ftdic, buf, 3)) {
+ ret = -8;
+ goto ftdi_err;
+ }
+
+ register_spi_master(&spi_master_ft2232);
+
+ return 0;
+
+ftdi_err:
+ if ((f = ftdi_usb_close(ftdic)) < 0) {
+ msg_perr("Unable to close FTDI device: %d (%s)\n", f, ftdi_get_error_string(ftdic));
+ }
+ return ret;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int ft2232_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ struct ftdi_context *ftdic = &ftdic_context;
+ static unsigned char *buf = NULL;
+ /* failed is special. We use bitwise ops, but it is essentially bool. */
+ int i = 0, ret = 0, failed = 0;
+ int bufsize;
+ static int oldbufsize = 0;
+
+ if (writecnt > 65536 || readcnt > 65536)
+ return SPI_INVALID_LENGTH;
+
+ /* buf is not used for the response from the chip. */
+ bufsize = max(writecnt + 9, 260 + 9);
+ /* Never shrink. realloc() calls are expensive. */
+ if (bufsize > oldbufsize) {
+ buf = realloc(buf, bufsize);
+ if (!buf) {
+ msg_perr("Out of memory!\n");
+ /* TODO: What to do with buf? */
+ return SPI_GENERIC_ERROR;
+ }
+ oldbufsize = bufsize;
+ }
+
+ /*
+ * Minimize USB transfers by packing as many commands as possible
+ * together. If we're not expecting to read, we can assert CS#, write,
+ * and deassert CS# all in one shot. If reading, we do three separate
+ * operations.
+ */
+ msg_pspew("Assert CS#\n");
+ buf[i++] = SET_BITS_LOW;
+ buf[i++] = 0 & ~cs_bits; /* assertive */
+ buf[i++] = pindir;
+
+ if (writecnt) {
+ buf[i++] = MPSSE_DO_WRITE | MPSSE_WRITE_NEG;
+ buf[i++] = (writecnt - 1) & 0xff;
+ buf[i++] = ((writecnt - 1) >> 8) & 0xff;
+ memcpy(buf + i, writearr, writecnt);
+ i += writecnt;
+ }
+
+ /*
+ * Optionally terminate this batch of commands with a
+ * read command, then do the fetch of the results.
+ */
+ if (readcnt) {
+ buf[i++] = MPSSE_DO_READ;
+ buf[i++] = (readcnt - 1) & 0xff;
+ buf[i++] = ((readcnt - 1) >> 8) & 0xff;
+ ret = send_buf(ftdic, buf, i);
+ failed = ret;
+ /* We can't abort here, we still have to deassert CS#. */
+ if (ret)
+ msg_perr("send_buf failed before read: %i\n", ret);
+ i = 0;
+ if (ret == 0) {
+ /*
+ * FIXME: This is unreliable. There's no guarantee that
+ * we read the response directly after sending the read
+ * command. We may be scheduled out etc.
+ */
+ ret = get_buf(ftdic, readarr, readcnt);
+ failed |= ret;
+ /* We can't abort here either. */
+ if (ret)
+ msg_perr("get_buf failed: %i\n", ret);
+ }
+ }
+
+ msg_pspew("De-assert CS#\n");
+ buf[i++] = SET_BITS_LOW;
+ buf[i++] = cs_bits;
+ buf[i++] = pindir;
+ ret = send_buf(ftdic, buf, i);
+ failed |= ret;
+ if (ret)
+ msg_perr("send_buf failed at end: %i\n", ret);
+
+ return failed ? -1 : 0;
+}
+
+#endif
diff --git a/gfxnvidia.c b/gfxnvidia.c
new file mode 100644
index 0000000..1e5a23a
--- /dev/null
+++ b/gfxnvidia.c
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+
+/* Mask to restrict flash accesses to a 128kB memory window.
+ * FIXME: Is this size a one-fits-all or card dependent?
+ */
+#define GFXNVIDIA_MEMMAP_MASK ((1 << 17) - 1)
+#define GFXNVIDIA_MEMMAP_SIZE (16 * 1024 * 1024)
+
+uint8_t *nvidia_bar;
+
+const struct dev_entry gfx_nvidia[] = {
+ {0x10de, 0x0010, NT, "NVIDIA", "Mutara V08 [NV2]" },
+ {0x10de, 0x0018, NT, "NVIDIA", "RIVA 128" },
+ {0x10de, 0x0020, NT, "NVIDIA", "RIVA TNT" },
+ {0x10de, 0x0028, NT, "NVIDIA", "RIVA TNT2/TNT2 Pro" },
+ {0x10de, 0x0029, NT, "NVIDIA", "RIVA TNT2 Ultra" },
+ {0x10de, 0x002c, NT, "NVIDIA", "Vanta/Vanta LT" },
+ {0x10de, 0x002d, OK, "NVIDIA", "RIVA TNT2 Model 64/Model 64 Pro" },
+ {0x10de, 0x00a0, NT, "NVIDIA", "Aladdin TNT2" },
+ {0x10de, 0x0100, NT, "NVIDIA", "GeForce 256" },
+ {0x10de, 0x0101, NT, "NVIDIA", "GeForce DDR" },
+ {0x10de, 0x0103, NT, "NVIDIA", "Quadro" },
+ {0x10de, 0x0110, NT, "NVIDIA", "GeForce2 MX" },
+ {0x10de, 0x0111, NT, "NVIDIA", "GeForce2 MX" },
+ {0x10de, 0x0112, NT, "NVIDIA", "GeForce2 GO" },
+ {0x10de, 0x0113, NT, "NVIDIA", "Quadro2 MXR" },
+ {0x10de, 0x0150, NT, "NVIDIA", "GeForce2 GTS/Pro" },
+ {0x10de, 0x0151, NT, "NVIDIA", "GeForce2 GTS" },
+ {0x10de, 0x0152, NT, "NVIDIA", "GeForce2 Ultra" },
+ {0x10de, 0x0153, NT, "NVIDIA", "Quadro2 Pro" },
+ {0x10de, 0x0200, NT, "NVIDIA", "GeForce 3 nFX" },
+ {0x10de, 0x0201, NT, "NVIDIA", "GeForce 3 nFX" },
+ {0x10de, 0x0202, NT, "NVIDIA", "GeForce 3 nFX Ultra" },
+ {0x10de, 0x0203, NT, "NVIDIA", "Quadro 3 DDC" },
+
+ {0},
+};
+
+static void gfxnvidia_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t gfxnvidia_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_gfxnvidia = {
+ .chip_readb = gfxnvidia_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = gfxnvidia_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int gfxnvidia_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t reg32;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(gfx_nvidia, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ io_base_addr += 0x300000;
+ msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);
+
+ nvidia_bar = rphysmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE);
+ if (nvidia_bar == ERROR_PTR)
+ return 1;
+
+ /* Allow access to flash interface (will disable screen). */
+ reg32 = pci_read_long(dev, 0x50);
+ reg32 &= ~(1 << 0);
+ rpci_write_long(dev, 0x50, reg32);
+
+ /* Write/erase doesn't work. */
+ programmer_may_write = 0;
+ register_par_master(&par_master_gfxnvidia, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void gfxnvidia_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ pci_mmio_writeb(val, nvidia_bar + (addr & GFXNVIDIA_MEMMAP_MASK));
+}
+
+static uint8_t gfxnvidia_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return pci_mmio_readb(nvidia_bar + (addr & GFXNVIDIA_MEMMAP_MASK));
+}
diff --git a/helpers.c b/helpers.c
new file mode 100644
index 0000000..f6eae46
--- /dev/null
+++ b/helpers.c
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009-2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+
+/* Returns the minimum number of bits needed to represent the given address.
+ * FIXME: use mind-blowing implementation. */
+uint32_t address_to_bits(uint32_t addr)
+{
+ unsigned int lzb = 0;
+ while (((1 << (31 - lzb)) & ~addr) != 0)
+ lzb++;
+ return 32 - lzb;
+}
+
+int bitcount(unsigned long a)
+{
+ int i = 0;
+ for (; a != 0; a >>= 1)
+ if (a & 1)
+ i++;
+ return i;
+}
+
+int max(int a, int b)
+{
+ return (a > b) ? a : b;
+}
+
+int min(int a, int b)
+{
+ return (a < b) ? a : b;
+}
+
+char *strcat_realloc(char *dest, const char *src)
+{
+ dest = realloc(dest, strlen(dest) + strlen(src) + 1);
+ if (!dest) {
+ msg_gerr("Out of memory!\n");
+ return NULL;
+ }
+ strcat(dest, src);
+ return dest;
+}
+
+void tolower_string(char *str)
+{
+ for (; *str != '\0'; str++)
+ *str = (char)tolower((unsigned char)*str);
+}
+
+/* FIXME: Find a better solution for MinGW. Maybe wrap strtok_s (C11) if it becomes available */
+#ifdef __MINGW32__
+char* strtok_r(char *str, const char *delim, char **nextp)
+{
+ if (str == NULL)
+ str = *nextp;
+
+ str += strspn(str, delim); /* Skip leading delimiters */
+ if (*str == '\0')
+ return NULL;
+
+ char *ret = str;
+ str += strcspn(str, delim); /* Find end of token */
+ if (*str != '\0')
+ *str++ = '\0';
+
+ *nextp = str;
+ return ret;
+}
+#endif
+
+/* There is no strnlen in DJGPP */
+#if defined(__DJGPP__) || !defined(HAVE_STRNLEN)
+size_t strnlen(const char *str, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n && str[i] != '\0'; i++)
+ ;
+ return i;
+}
+#endif
diff --git a/hwaccess.c b/hwaccess.c
new file mode 100644
index 0000000..1901ee6
--- /dev/null
+++ b/hwaccess.c
@@ -0,0 +1,305 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "platform.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#if !defined (__DJGPP__) && !defined(__LIBPAYLOAD__)
+/* No file access needed/possible to get hardware access permissions. */
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#include "flash.h"
+#include "hwaccess.h"
+
+#if !(IS_LINUX || IS_MACOSX || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__DJGPP__) || defined(__LIBPAYLOAD__) || defined(__sun) || defined(__gnu_hurd__))
+#error "Unknown operating system"
+#endif
+
+#define USE_IOPL (IS_LINUX || IS_MACOSX || defined(__NetBSD__) || defined(__OpenBSD__))
+#define USE_DEV_IO (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__))
+#define USE_IOPERM (defined(__gnu_hurd__))
+
+#if USE_IOPERM
+#include <sys/io.h>
+#endif
+
+#if IS_X86 && USE_DEV_IO
+int io_fd;
+#endif
+
+/* Prevent reordering and/or merging of reads/writes to hardware.
+ * Such reordering and/or merging would break device accesses which depend on the exact access order.
+ */
+static inline void sync_primitive(void)
+{
+/* This is not needed for...
+ * - x86: uses uncached accesses which have a strongly ordered memory model.
+ * - MIPS: uses uncached accesses in mode 2 on /dev/mem which has also a strongly ordered memory model.
+ * - ARM: uses a strongly ordered memory model for device memories.
+ *
+ * See also https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/memory-barriers.txt
+ */
+#if IS_PPC // cf. http://lxr.free-electrons.com/source/arch/powerpc/include/asm/barrier.h
+ asm("eieio" : : : "memory");
+#elif IS_SPARC
+#if defined(__sparc_v9__) || defined(__sparcv9)
+ /* Sparc V9 CPUs support three different memory orderings that range from x86-like TSO to PowerPC-like
+ * RMO. The modes can be switched at runtime thus to make sure we maintain the right order of access we
+ * use the strongest hardware memory barriers that exist on Sparc V9. */
+ asm volatile ("membar #Sync" ::: "memory");
+#elif defined(__sparc_v8__) || defined(__sparcv8)
+ /* On SPARC V8 there is no RMO just PSO and that does not apply to I/O accesses... but if V8 code is run
+ * on V9 CPUs it might apply... or not... we issue a write barrier anyway. That's the most suitable
+ * operation in the V8 instruction set anyway. If you know better then please tell us. */
+ asm volatile ("stbar");
+#else
+ #error Unknown and/or unsupported SPARC instruction set version detected.
+#endif
+#endif
+}
+
+#if IS_X86 && !(defined(__DJGPP__) || defined(__LIBPAYLOAD__))
+static int release_io_perms(void *p)
+{
+#if defined (__sun)
+ sysi86(SI86V86, V86SC_IOPL, 0);
+#elif USE_DEV_IO
+ close(io_fd);
+#elif USE_IOPERM
+ ioperm(0, 65536, 0);
+#elif USE_IOPL
+ iopl(0);
+#endif
+ return 0;
+}
+#endif
+
+/* Get I/O permissions with automatic permission release on shutdown. */
+int rget_io_perms(void)
+{
+#if IS_X86 && !(defined(__DJGPP__) || defined(__LIBPAYLOAD__))
+#if defined (__sun)
+ if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0) {
+#elif USE_DEV_IO
+ if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
+#elif USE_IOPERM
+ if (ioperm(0, 65536, 1) != 0) {
+#elif USE_IOPL
+ if (iopl(3) != 0) {
+#endif
+ msg_perr("ERROR: Could not get I/O privileges (%s).\n", strerror(errno));
+ msg_perr("You need to be root.\n");
+#if defined (__OpenBSD__)
+ msg_perr("If you are root already please set securelevel=-1 in /etc/rc.securelevel and\n"
+ "reboot, or reboot into single user mode.\n");
+#elif defined(__NetBSD__)
+ msg_perr("If you are root already please reboot into single user mode or make sure\n"
+ "that your kernel configuration has the option INSECURE enabled.\n");
+#endif
+ return 1;
+ } else {
+ register_shutdown(release_io_perms, NULL);
+ }
+#else
+ /* DJGPP and libpayload environments have full PCI port I/O permissions by default. */
+ /* PCI port I/O support is unimplemented on PPC/MIPS and unavailable on ARM. */
+#endif
+ return 0;
+}
+
+void mmio_writeb(uint8_t val, void *addr)
+{
+ *(volatile uint8_t *) addr = val;
+ sync_primitive();
+}
+
+void mmio_writew(uint16_t val, void *addr)
+{
+ *(volatile uint16_t *) addr = val;
+ sync_primitive();
+}
+
+void mmio_writel(uint32_t val, void *addr)
+{
+ *(volatile uint32_t *) addr = val;
+ sync_primitive();
+}
+
+uint8_t mmio_readb(void *addr)
+{
+ return *(volatile uint8_t *) addr;
+}
+
+uint16_t mmio_readw(void *addr)
+{
+ return *(volatile uint16_t *) addr;
+}
+
+uint32_t mmio_readl(void *addr)
+{
+ return *(volatile uint32_t *) addr;
+}
+
+void mmio_readn(void *addr, uint8_t *buf, size_t len)
+{
+ memcpy(buf, addr, len);
+ return;
+}
+
+void mmio_le_writeb(uint8_t val, void *addr)
+{
+ mmio_writeb(cpu_to_le8(val), addr);
+}
+
+void mmio_le_writew(uint16_t val, void *addr)
+{
+ mmio_writew(cpu_to_le16(val), addr);
+}
+
+void mmio_le_writel(uint32_t val, void *addr)
+{
+ mmio_writel(cpu_to_le32(val), addr);
+}
+
+uint8_t mmio_le_readb(void *addr)
+{
+ return le_to_cpu8(mmio_readb(addr));
+}
+
+uint16_t mmio_le_readw(void *addr)
+{
+ return le_to_cpu16(mmio_readw(addr));
+}
+
+uint32_t mmio_le_readl(void *addr)
+{
+ return le_to_cpu32(mmio_readl(addr));
+}
+
+enum mmio_write_type {
+ mmio_write_type_b,
+ mmio_write_type_w,
+ mmio_write_type_l,
+};
+
+struct undo_mmio_write_data {
+ void *addr;
+ int reg;
+ enum mmio_write_type type;
+ union {
+ uint8_t bdata;
+ uint16_t wdata;
+ uint32_t ldata;
+ };
+};
+
+int undo_mmio_write(void *p)
+{
+ struct undo_mmio_write_data *data = p;
+ msg_pdbg("Restoring MMIO space at %p\n", data->addr);
+ switch (data->type) {
+ case mmio_write_type_b:
+ mmio_writeb(data->bdata, data->addr);
+ break;
+ case mmio_write_type_w:
+ mmio_writew(data->wdata, data->addr);
+ break;
+ case mmio_write_type_l:
+ mmio_writel(data->ldata, data->addr);
+ break;
+ }
+ /* p was allocated in register_undo_mmio_write. */
+ free(p);
+ return 0;
+}
+
+#define register_undo_mmio_write(a, c) \
+{ \
+ struct undo_mmio_write_data *undo_mmio_write_data; \
+ undo_mmio_write_data = malloc(sizeof(struct undo_mmio_write_data)); \
+ if (!undo_mmio_write_data) { \
+ msg_gerr("Out of memory!\n"); \
+ exit(1); \
+ } \
+ undo_mmio_write_data->addr = a; \
+ undo_mmio_write_data->type = mmio_write_type_##c; \
+ undo_mmio_write_data->c##data = mmio_read##c(a); \
+ register_shutdown(undo_mmio_write, undo_mmio_write_data); \
+}
+
+#define register_undo_mmio_writeb(a) register_undo_mmio_write(a, b)
+#define register_undo_mmio_writew(a) register_undo_mmio_write(a, w)
+#define register_undo_mmio_writel(a) register_undo_mmio_write(a, l)
+
+void rmmio_writeb(uint8_t val, void *addr)
+{
+ register_undo_mmio_writeb(addr);
+ mmio_writeb(val, addr);
+}
+
+void rmmio_writew(uint16_t val, void *addr)
+{
+ register_undo_mmio_writew(addr);
+ mmio_writew(val, addr);
+}
+
+void rmmio_writel(uint32_t val, void *addr)
+{
+ register_undo_mmio_writel(addr);
+ mmio_writel(val, addr);
+}
+
+void rmmio_le_writeb(uint8_t val, void *addr)
+{
+ register_undo_mmio_writeb(addr);
+ mmio_le_writeb(val, addr);
+}
+
+void rmmio_le_writew(uint16_t val, void *addr)
+{
+ register_undo_mmio_writew(addr);
+ mmio_le_writew(val, addr);
+}
+
+void rmmio_le_writel(uint32_t val, void *addr)
+{
+ register_undo_mmio_writel(addr);
+ mmio_le_writel(val, addr);
+}
+
+void rmmio_valb(void *addr)
+{
+ register_undo_mmio_writeb(addr);
+}
+
+void rmmio_valw(void *addr)
+{
+ register_undo_mmio_writew(addr);
+}
+
+void rmmio_vall(void *addr)
+{
+ register_undo_mmio_writel(addr);
+}
diff --git a/hwaccess.h b/hwaccess.h
new file mode 100644
index 0000000..b006d05
--- /dev/null
+++ b/hwaccess.h
@@ -0,0 +1,388 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Header file for hardware access and OS abstraction. Included from flash.h.
+ */
+
+#ifndef __HWACCESS_H__
+#define __HWACCESS_H__ 1
+
+#include "platform.h"
+
+#if NEED_PCI == 1
+/*
+ * libpci headers use the variable name "index" which triggers shadowing
+ * warnings on systems which have the index() function in a default #include
+ * or as builtin.
+ */
+#define index shadow_workaround_index
+
+#if !defined (__NetBSD__)
+#include <pci/pci.h>
+#else
+#include <pciutils/pci.h>
+#endif
+
+#undef index
+#endif /* NEED_PCI == 1 */
+
+
+/* The next big hunk tries to guess endianess from various preprocessor macros */
+/* First some error checking in case some weird header has defined both.
+ * NB: OpenBSD always defines _BIG_ENDIAN and _LITTLE_ENDIAN. */
+#if defined (__LITTLE_ENDIAN__) && defined (__BIG_ENDIAN__)
+#error Conflicting endianness #define
+#endif
+
+#if IS_X86
+
+/* All x86 is little-endian. */
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+
+#elif IS_MIPS
+
+/* MIPS can be either endian. */
+#if defined (__MIPSEL) || defined (__MIPSEL__) || defined (_MIPSEL) || defined (MIPSEL)
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#elif defined (__MIPSEB) || defined (__MIPSEB__) || defined (_MIPSEB) || defined (MIPSEB)
+#define __FLASHROM_BIG_ENDIAN__ 1
+#endif
+
+#elif IS_PPC
+
+/* PowerPC can be either endian. */
+#if defined (_BIG_ENDIAN) || defined (__BIG_ENDIAN__)
+#define __FLASHROM_BIG_ENDIAN__ 1
+#elif defined (_LITTLE_ENDIAN) || defined (__LITTLE_ENDIAN__)
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#endif
+
+#elif IS_ARM
+
+/* ARM can be either endian. */
+#if defined (__ARMEB__)
+#define __FLASHROM_BIG_ENDIAN__ 1
+#elif defined (__ARMEL__)
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#endif
+
+#elif IS_SPARC
+/* SPARC is big endian in general (but allows to access data in little endian too). */
+#define __FLASHROM_BIG_ENDIAN__ 1
+
+#endif /* IS_? */
+
+#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
+
+/* If architecture-specific approaches fail try generic variants. First: BSD (works about everywhere). */
+#if !IS_WINDOWS
+#include <sys/param.h>
+
+#if defined (__BYTE_ORDER)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __FLASHROM_LITTLE_ENDIAN__
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define __FLASHROM_BIG_ENDIAN__
+#else
+#error Unknown byte order!
+#endif
+#endif /* defined __BYTE_ORDER */
+#endif /* !IS_WINDOWS */
+
+#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
+
+/* Nonstandard libc-specific macros for determining endianness. */
+/* musl provides an endian.h as well... but it can not be detected from within C. */
+#if defined(__GLIBC__)
+#include <endian.h>
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define __FLASHROM_LITTLE_ENDIAN__ 1
+#elif BYTE_ORDER == BIG_ENDIAN
+#define __FLASHROM_BIG_ENDIAN__ 1
+#endif
+#endif
+#endif
+
+#endif
+
+#if !defined (__FLASHROM_BIG_ENDIAN__) && !defined (__FLASHROM_LITTLE_ENDIAN__)
+#error Unable to determine endianness.
+#endif
+
+#define ___constant_swab8(x) ((uint8_t) ( \
+ (((uint8_t)(x) & (uint8_t)0xffU))))
+
+#define ___constant_swab16(x) ((uint16_t) ( \
+ (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \
+ (((uint16_t)(x) & (uint16_t)0xff00U) >> 8)))
+
+#define ___constant_swab32(x) ((uint32_t) ( \
+ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
+
+#define ___constant_swab64(x) ((uint64_t) ( \
+ (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+ (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
+
+#if defined (__FLASHROM_BIG_ENDIAN__)
+
+#define cpu_to_le(bits) \
+static inline uint##bits##_t cpu_to_le##bits(uint##bits##_t val) \
+{ \
+ return ___constant_swab##bits(val); \
+}
+
+cpu_to_le(8)
+cpu_to_le(16)
+cpu_to_le(32)
+cpu_to_le(64)
+
+#define cpu_to_be8
+#define cpu_to_be16
+#define cpu_to_be32
+#define cpu_to_be64
+
+#elif defined (__FLASHROM_LITTLE_ENDIAN__)
+
+#define cpu_to_be(bits) \
+static inline uint##bits##_t cpu_to_be##bits(uint##bits##_t val) \
+{ \
+ return ___constant_swab##bits(val); \
+}
+
+cpu_to_be(8)
+cpu_to_be(16)
+cpu_to_be(32)
+cpu_to_be(64)
+
+#define cpu_to_le8
+#define cpu_to_le16
+#define cpu_to_le32
+#define cpu_to_le64
+
+#endif /* __FLASHROM_BIG_ENDIAN__ / __FLASHROM_LITTLE_ENDIAN__ */
+
+#define be_to_cpu8 cpu_to_be8
+#define be_to_cpu16 cpu_to_be16
+#define be_to_cpu32 cpu_to_be32
+#define be_to_cpu64 cpu_to_be64
+#define le_to_cpu8 cpu_to_le8
+#define le_to_cpu16 cpu_to_le16
+#define le_to_cpu32 cpu_to_le32
+#define le_to_cpu64 cpu_to_le64
+
+#if NEED_RAW_ACCESS == 1
+#if IS_X86
+
+/* sys/io.h provides iopl(2) and x86 I/O port access functions (inb, outb etc).
+ * It is included in glibc (thus available also on debian/kFreeBSD) but also in other libcs that mimic glibc,
+ * e.g. musl and uclibc. Because we cannot detect the libc or existence of the header or of the instructions
+ * themselves safely in here we use some heuristic below:
+ * On Android we don't have the header file and no way for I/O port access at all. However, sys/glibc-syscalls.h
+ * refers to an iopl implementation and we therefore include at least that one for now. On non-Android we assume
+ * that a Linux system's libc has a suitable sys/io.h or (on non-Linux) we depend on glibc to offer it. */
+#if defined(__ANDROID__)
+#include <sys/glibc-syscalls.h>
+#elif defined(__linux__) || defined(__GLIBC__)
+#include <sys/io.h>
+#endif
+
+#define __FLASHROM_HAVE_OUTB__ 1
+
+/* for iopl and outb under Solaris */
+#if defined (__sun)
+#include <sys/sysi86.h>
+#include <sys/psw.h>
+#include <asm/sunddi.h>
+#endif
+
+/* Clarification about OUTB/OUTW/OUTL argument order:
+ * OUT[BWL](val, port)
+ */
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ /* Note that Debian/kFreeBSD (FreeBSD kernel with glibc) has conflicting
+ * out[bwl] definitions in machine/cpufunc.h and sys/io.h at least in some
+ * versions. Use machine/cpufunc.h only for plain FreeBSD/DragonFlyBSD.
+ */
+ #include <sys/types.h>
+ #include <machine/cpufunc.h>
+ #define OUTB(x, y) do { u_int outb_tmp = (y); outb(outb_tmp, (x)); } while (0)
+ #define OUTW(x, y) do { u_int outw_tmp = (y); outw(outw_tmp, (x)); } while (0)
+ #define OUTL(x, y) do { u_int outl_tmp = (y); outl(outl_tmp, (x)); } while (0)
+ #define INB(x) __extension__ ({ u_int inb_tmp = (x); inb(inb_tmp); })
+ #define INW(x) __extension__ ({ u_int inw_tmp = (x); inw(inw_tmp); })
+ #define INL(x) __extension__ ({ u_int inl_tmp = (x); inl(inl_tmp); })
+#else
+
+#if defined (__sun)
+ /* Note different order for outb */
+ #define OUTB(x,y) outb(y, x)
+ #define OUTW(x,y) outw(y, x)
+ #define OUTL(x,y) outl(y, x)
+ #define INB inb
+ #define INW inw
+ #define INL inl
+#else
+
+#ifdef __DJGPP__
+
+#include <pc.h>
+
+ #define OUTB(x,y) outportb(y, x)
+ #define OUTW(x,y) outportw(y, x)
+ #define OUTL(x,y) outportl(y, x)
+
+ #define INB inportb
+ #define INW inportw
+ #define INL inportl
+
+#else
+
+#if defined(__MACH__) && defined(__APPLE__)
+ /* Header is part of the DirectHW library. */
+ #include <DirectHW/DirectHW.h>
+#endif
+
+ /* This is the usual glibc interface. */
+ #define OUTB outb
+ #define OUTW outw
+ #define OUTL outl
+ #define INB inb
+ #define INW inw
+ #define INL inl
+#endif
+#endif
+#endif
+
+#if defined(__NetBSD__) || defined (__OpenBSD__)
+ #if defined(__i386__) || defined(__x86_64__)
+ #include <sys/types.h>
+ #include <machine/sysarch.h>
+ #if defined(__NetBSD__)
+ #if defined(__i386__)
+ #define iopl i386_iopl
+ #elif defined(__x86_64__)
+ #define iopl x86_64_iopl
+ #endif
+ #elif defined (__OpenBSD__)
+ #if defined(__i386__)
+ #define iopl i386_iopl
+ #elif defined(__amd64__)
+ #define iopl amd64_iopl
+ #endif
+ #endif
+
+static inline void outb(uint8_t value, uint16_t port)
+{
+ __asm__ volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+ uint8_t value;
+ __asm__ volatile ("inb %w1,%0":"=a" (value):"Nd" (port));
+ return value;
+}
+
+static inline void outw(uint16_t value, uint16_t port)
+{
+ __asm__ volatile ("outw %w0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint16_t inw(uint16_t port)
+{
+ uint16_t value;
+ __asm__ volatile ("inw %w1,%0":"=a" (value):"Nd" (port));
+ return value;
+}
+
+static inline void outl(uint32_t value, uint16_t port)
+{
+ __asm__ volatile ("outl %0,%w1": :"a" (value), "Nd" (port));
+}
+
+static inline uint32_t inl(uint16_t port)
+{
+ uint32_t value;
+ __asm__ volatile ("inl %1,%0":"=a" (value):"Nd" (port));
+ return value;
+}
+ #endif
+#endif
+
+#if !(defined(__MACH__) && defined(__APPLE__)) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) && !defined(__DragonFly__) && !defined(__LIBPAYLOAD__)
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t rdmsr(int addr);
+int wrmsr(int addr, msr_t msr);
+#endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+/* FreeBSD already has conflicting definitions for wrmsr/rdmsr. */
+#undef rdmsr
+#undef wrmsr
+#define rdmsr freebsd_rdmsr
+#define wrmsr freebsd_wrmsr
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t freebsd_rdmsr(int addr);
+int freebsd_wrmsr(int addr, msr_t msr);
+#endif
+#if defined(__LIBPAYLOAD__)
+#include <arch/io.h>
+#include <arch/msr.h>
+typedef struct { uint32_t hi, lo; } msr_t;
+msr_t libpayload_rdmsr(int addr);
+int libpayload_wrmsr(int addr, msr_t msr);
+#undef rdmsr
+#define rdmsr libpayload_rdmsr
+#define wrmsr libpayload_wrmsr
+#endif
+
+#elif IS_PPC
+
+/* PCI port I/O is not yet implemented on PowerPC. */
+
+#elif IS_MIPS
+
+/* PCI port I/O is not yet implemented on MIPS. */
+
+#elif IS_SPARC
+
+/* PCI port I/O is not yet implemented on SPARC. */
+
+#elif IS_ARM
+
+/* Non memory mapped I/O is not supported on ARM. */
+
+#else
+
+#error Unknown architecture, please check if it supports PCI port IO.
+
+#endif /* IS_* */
+#endif /* NEED_RAW_ACCESS == 1 */
+
+#endif /* !__HWACCESS_H__ */
diff --git a/ich_descriptors.c b/ich_descriptors.c
new file mode 100644
index 0000000..90f70ee
--- /dev/null
+++ b/ich_descriptors.c
@@ -0,0 +1,926 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (c) 2010 Matthias Wenzel <bios at mazzoo dot de>
+ * Copyright (c) 2011 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "ich_descriptors.h"
+
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+#include <stdio.h>
+#define print(t, ...) printf(__VA_ARGS__)
+#define DESCRIPTOR_MODE_SIGNATURE 0x0ff0a55a
+/* The upper map is located in the word before the 256B-long OEM section at the
+ * end of the 4kB-long flash descriptor.
+ */
+#define UPPER_MAP_OFFSET (4096 - 256 - 4)
+#define getVTBA(flumap) (((flumap)->FLUMAP1 << 4) & 0x00000ff0)
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
+#include "flash.h" /* for msg_* */
+#include "programmer.h"
+
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+
+#ifndef min
+#define min(a, b) (a < b) ? a : b
+#endif
+
+void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity, bool print_vcl)
+{
+ print(verbosity, "BES=0x%x, ", (reg_val & VSCC_BES) >> VSCC_BES_OFF);
+ print(verbosity, "WG=%d, ", (reg_val & VSCC_WG) >> VSCC_WG_OFF);
+ print(verbosity, "WSR=%d, ", (reg_val & VSCC_WSR) >> VSCC_WSR_OFF);
+ print(verbosity, "WEWS=%d, ", (reg_val & VSCC_WEWS) >> VSCC_WEWS_OFF);
+ print(verbosity, "EO=0x%x", (reg_val & VSCC_EO) >> VSCC_EO_OFF);
+ if (print_vcl)
+ print(verbosity, ", VCL=%d", (reg_val & VSCC_VCL) >> VSCC_VCL_OFF);
+ print(verbosity, "\n");
+}
+
+#define getFCBA(cont) (((cont)->FLMAP0 << 4) & 0x00000ff0)
+#define getFRBA(cont) (((cont)->FLMAP0 >> 12) & 0x00000ff0)
+#define getFMBA(cont) (((cont)->FLMAP1 << 4) & 0x00000ff0)
+#define getFISBA(cont) (((cont)->FLMAP1 >> 12) & 0x00000ff0)
+#define getFMSBA(cont) (((cont)->FLMAP2 << 4) & 0x00000ff0)
+
+void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc)
+{
+ prettyprint_ich_descriptor_content(&desc->content);
+ prettyprint_ich_descriptor_component(cs, desc);
+ prettyprint_ich_descriptor_region(desc);
+ prettyprint_ich_descriptor_master(&desc->master);
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+ if (cs >= CHIPSET_ICH8) {
+ prettyprint_ich_descriptor_upper_map(&desc->upper);
+ prettyprint_ich_descriptor_straps(cs, desc);
+ }
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+}
+
+void prettyprint_ich_descriptor_content(const struct ich_desc_content *cont)
+{
+ msg_pdbg2("=== Content Section ===\n");
+ msg_pdbg2("FLVALSIG 0x%08x\n", cont->FLVALSIG);
+ msg_pdbg2("FLMAP0 0x%08x\n", cont->FLMAP0);
+ msg_pdbg2("FLMAP1 0x%08x\n", cont->FLMAP1);
+ msg_pdbg2("FLMAP2 0x%08x\n", cont->FLMAP2);
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- Details ---\n");
+ msg_pdbg2("NR (Number of Regions): %5d\n", cont->NR + 1);
+ msg_pdbg2("FRBA (Flash Region Base Address): 0x%03x\n", getFRBA(cont));
+ msg_pdbg2("NC (Number of Components): %5d\n", cont->NC + 1);
+ msg_pdbg2("FCBA (Flash Component Base Address): 0x%03x\n", getFCBA(cont));
+ msg_pdbg2("ISL (ICH/PCH Strap Length): %5d\n", cont->ISL);
+ msg_pdbg2("FISBA/FPSBA (Flash ICH/PCH Strap Base Address): 0x%03x\n", getFISBA(cont));
+ msg_pdbg2("NM (Number of Masters): %5d\n", cont->NM + 1);
+ msg_pdbg2("FMBA (Flash Master Base Address): 0x%03x\n", getFMBA(cont));
+ msg_pdbg2("MSL/PSL (MCH/PROC Strap Length): %5d\n", cont->MSL);
+ msg_pdbg2("FMSBA (Flash MCH/PROC Strap Base Address): 0x%03x\n", getFMSBA(cont));
+ msg_pdbg2("\n");
+}
+
+static const char *pprint_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx)
+{
+ if (idx > 1) {
+ msg_perr("Only ICH SPI component index 0 or 1 are supported yet.\n");
+ return NULL;
+ }
+
+ if (desc->content.NC == 0 && idx > 0)
+ return "unused";
+
+ static const char * const size_str[] = {
+ "512 kB", /* 0000 */
+ "1 MB", /* 0001 */
+ "2 MB", /* 0010 */
+ "4 MB", /* 0011 */
+ "8 MB", /* 0100 */
+ "16 MB", /* 0101 */ /* Maximum up to Lynx Point (excl.) */
+ "32 MB", /* 0110 */
+ "64 MB", /* 0111 */
+ };
+
+ switch (cs) {
+ case CHIPSET_ICH8:
+ case CHIPSET_ICH9:
+ case CHIPSET_ICH10:
+ case CHIPSET_5_SERIES_IBEX_PEAK:
+ case CHIPSET_6_SERIES_COUGAR_POINT:
+ case CHIPSET_7_SERIES_PANTHER_POINT:
+ case CHIPSET_BAYTRAIL: {
+ uint8_t size_enc;
+ if (idx == 0) {
+ size_enc = desc->component.dens_old.comp1_density;
+ } else {
+ size_enc = desc->component.dens_old.comp2_density;
+ }
+ if (size_enc > 5)
+ return "reserved";
+ return size_str[size_enc];
+ }
+ case CHIPSET_8_SERIES_LYNX_POINT:
+ case CHIPSET_8_SERIES_LYNX_POINT_LP:
+ case CHIPSET_8_SERIES_WELLSBURG:
+ case CHIPSET_9_SERIES_WILDCAT_POINT: {
+ uint8_t size_enc;
+ if (idx == 0) {
+ size_enc = desc->component.dens_new.comp1_density;
+ } else {
+ size_enc = desc->component.dens_new.comp2_density;
+ }
+ if (size_enc > 7)
+ return "reserved";
+ return size_str[size_enc];
+ }
+ case CHIPSET_ICH_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+static const char *pprint_freq(enum ich_chipset cs, uint8_t value)
+{
+ static const char * const freq_str[8] = {
+ "20 MHz", /* 000 */
+ "33 MHz", /* 001 */
+ "reserved", /* 010 */
+ "reserved", /* 011 */
+ "50 MHz", /* 100 */ /* New since Ibex Peak */
+ "reserved", /* 101 */
+ "reserved", /* 110 */
+ "reserved" /* 111 */
+ };
+
+ switch (cs) {
+ case CHIPSET_ICH8:
+ case CHIPSET_ICH9:
+ case CHIPSET_ICH10:
+ if (value > 1)
+ return "reserved";
+ case CHIPSET_5_SERIES_IBEX_PEAK:
+ case CHIPSET_6_SERIES_COUGAR_POINT:
+ case CHIPSET_7_SERIES_PANTHER_POINT:
+ case CHIPSET_8_SERIES_LYNX_POINT:
+ case CHIPSET_BAYTRAIL:
+ case CHIPSET_8_SERIES_LYNX_POINT_LP:
+ case CHIPSET_8_SERIES_WELLSBURG:
+ case CHIPSET_9_SERIES_WILDCAT_POINT:
+ return freq_str[value];
+ case CHIPSET_ICH_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+void prettyprint_ich_descriptor_component(enum ich_chipset cs, const struct ich_descriptors *desc)
+{
+
+ msg_pdbg2("=== Component Section ===\n");
+ msg_pdbg2("FLCOMP 0x%08x\n", desc->component.FLCOMP);
+ msg_pdbg2("FLILL 0x%08x\n", desc->component.FLILL );
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- Details ---\n");
+ msg_pdbg2("Component 1 density: %s\n", pprint_density(cs, desc, 0));
+ if (desc->content.NC)
+ msg_pdbg2("Component 2 density: %s\n", pprint_density(cs, desc, 1));
+ else
+ msg_pdbg2("Component 2 is not used.\n");
+ msg_pdbg2("Read Clock Frequency: %s\n", pprint_freq(cs, desc->component.modes.freq_read));
+ msg_pdbg2("Read ID and Status Clock Freq.: %s\n", pprint_freq(cs, desc->component.modes.freq_read_id));
+ msg_pdbg2("Write and Erase Clock Freq.: %s\n", pprint_freq(cs, desc->component.modes.freq_write));
+ msg_pdbg2("Fast Read is %ssupported.\n", desc->component.modes.fastread ? "" : "not ");
+ if (desc->component.modes.fastread)
+ msg_pdbg2("Fast Read Clock Frequency: %s\n",
+ pprint_freq(cs, desc->component.modes.freq_fastread));
+ if (cs > CHIPSET_6_SERIES_COUGAR_POINT)
+ msg_pdbg2("Dual Output Fast Read Support: %sabled\n",
+ desc->component.modes.dual_output ? "dis" : "en");
+ if (desc->component.FLILL == 0)
+ msg_pdbg2("No forbidden opcodes.\n");
+ else {
+ msg_pdbg2("Invalid instruction 0: 0x%02x\n",
+ desc->component.invalid_instr0);
+ msg_pdbg2("Invalid instruction 1: 0x%02x\n",
+ desc->component.invalid_instr1);
+ msg_pdbg2("Invalid instruction 2: 0x%02x\n",
+ desc->component.invalid_instr2);
+ msg_pdbg2("Invalid instruction 3: 0x%02x\n",
+ desc->component.invalid_instr3);
+ }
+ msg_pdbg2("\n");
+}
+
+static void pprint_freg(const struct ich_desc_region *reg, uint32_t i)
+{
+ static const char *const region_names[5] = {
+ "Descr.", "BIOS", "ME", "GbE", "Platf."
+ };
+ if (i >= 5) {
+ msg_pdbg2("%s: region index too high.\n", __func__);
+ return;
+ }
+ uint32_t base = ICH_FREG_BASE(reg->FLREGs[i]);
+ uint32_t limit = ICH_FREG_LIMIT(reg->FLREGs[i]);
+ msg_pdbg2("Region %d (%-6s) ", i, region_names[i]);
+ if (base > limit)
+ msg_pdbg2("is unused.\n");
+ else
+ msg_pdbg2("0x%08x - 0x%08x\n", base, limit | 0x0fff);
+}
+
+void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc)
+{
+ uint8_t i;
+ uint8_t nr = desc->content.NR + 1;
+ msg_pdbg2("=== Region Section ===\n");
+ if (nr > 5) {
+ msg_pdbg2("%s: number of regions too high (%d).\n", __func__,
+ nr);
+ return;
+ }
+ for (i = 0; i < 5; i++)
+ msg_pdbg2("FLREG%d 0x%08x\n", i, desc->region.FLREGs[i]);
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- Details ---\n");
+ for (i = 0; i < 5; i++)
+ pprint_freg(&desc->region, i);
+ msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_master(const struct ich_desc_master *mstr)
+{
+ msg_pdbg2("=== Master Section ===\n");
+ msg_pdbg2("FLMSTR1 0x%08x\n", mstr->FLMSTR1);
+ msg_pdbg2("FLMSTR2 0x%08x\n", mstr->FLMSTR2);
+ msg_pdbg2("FLMSTR3 0x%08x\n", mstr->FLMSTR3);
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- Details ---\n");
+ msg_pdbg2(" Descr. BIOS ME GbE Platf.\n");
+ msg_pdbg2("BIOS %c%c %c%c %c%c %c%c %c%c\n",
+ (mstr->BIOS_descr_r) ?'r':' ', (mstr->BIOS_descr_w) ?'w':' ',
+ (mstr->BIOS_BIOS_r) ?'r':' ', (mstr->BIOS_BIOS_w) ?'w':' ',
+ (mstr->BIOS_ME_r) ?'r':' ', (mstr->BIOS_ME_w) ?'w':' ',
+ (mstr->BIOS_GbE_r) ?'r':' ', (mstr->BIOS_GbE_w) ?'w':' ',
+ (mstr->BIOS_plat_r) ?'r':' ', (mstr->BIOS_plat_w) ?'w':' ');
+ msg_pdbg2("ME %c%c %c%c %c%c %c%c %c%c\n",
+ (mstr->ME_descr_r) ?'r':' ', (mstr->ME_descr_w) ?'w':' ',
+ (mstr->ME_BIOS_r) ?'r':' ', (mstr->ME_BIOS_w) ?'w':' ',
+ (mstr->ME_ME_r) ?'r':' ', (mstr->ME_ME_w) ?'w':' ',
+ (mstr->ME_GbE_r) ?'r':' ', (mstr->ME_GbE_w) ?'w':' ',
+ (mstr->ME_plat_r) ?'r':' ', (mstr->ME_plat_w) ?'w':' ');
+ msg_pdbg2("GbE %c%c %c%c %c%c %c%c %c%c\n",
+ (mstr->GbE_descr_r) ?'r':' ', (mstr->GbE_descr_w) ?'w':' ',
+ (mstr->GbE_BIOS_r) ?'r':' ', (mstr->GbE_BIOS_w) ?'w':' ',
+ (mstr->GbE_ME_r) ?'r':' ', (mstr->GbE_ME_w) ?'w':' ',
+ (mstr->GbE_GbE_r) ?'r':' ', (mstr->GbE_GbE_w) ?'w':' ',
+ (mstr->GbE_plat_r) ?'r':' ', (mstr->GbE_plat_w) ?'w':' ');
+ msg_pdbg2("\n");
+}
+
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+void prettyprint_ich_descriptor_straps_ich8(const struct ich_descriptors *desc)
+{
+ static const char * const str_GPIO12[4] = {
+ "GPIO12",
+ "LAN PHY Power Control Function (Native Output)",
+ "GLAN_DOCK# (Native Input)",
+ "invalid configuration",
+ };
+
+ msg_pdbg2("--- MCH details ---\n");
+ msg_pdbg2("ME B is %sabled.\n", desc->north.ich8.MDB ? "dis" : "en");
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- ICH details ---\n");
+ msg_pdbg2("ME SMBus Address 1: 0x%02x\n", desc->south.ich8.ASD);
+ msg_pdbg2("ME SMBus Address 2: 0x%02x\n", desc->south.ich8.ASD2);
+ msg_pdbg2("ME SMBus Controller is connected to the %s.\n",
+ desc->south.ich8.MESM2SEL ? "SMLink pins" : "SMBus pins");
+ msg_pdbg2("SPI CS1 is used for %s.\n",
+ desc->south.ich8.SPICS1_LANPHYPC_SEL ?
+ "LAN PHY Power Control Function" :
+ "SPI Chip Select");
+ msg_pdbg2("GPIO12 is used as %s.\n",
+ str_GPIO12[desc->south.ich8.GPIO12_SEL]);
+ msg_pdbg2("PCIe Port 6 is used for %s.\n",
+ desc->south.ich8.GLAN_PCIE_SEL ? "integrated LAN" : "PCI Express");
+ msg_pdbg2("%sn BMC Mode: "
+ "Intel AMT SMBus Controller 1 is connected to %s.\n",
+ desc->south.ich8.BMCMODE ? "I" : "Not i",
+ desc->south.ich8.BMCMODE ? "SMLink" : "SMBus");
+ msg_pdbg2("TCO is in %s Mode.\n",
+ desc->south.ich8.TCOMODE ? "Advanced TCO" : "Legacy/Compatible");
+ msg_pdbg2("ME A is %sabled.\n",
+ desc->south.ich8.ME_DISABLE ? "dis" : "en");
+ msg_pdbg2("\n");
+}
+
+static void prettyprint_ich_descriptor_straps_56_pciecs(uint8_t conf, uint8_t off)
+{
+ msg_pdbg2("PCI Express Port Configuration Strap %d: ", off+1);
+
+ off *= 4;
+ switch (conf){
+ case 0:
+ msg_pdbg2("4x1 Ports %d-%d (x1)", 1+off, 4+off);
+ break;
+ case 1:
+ msg_pdbg2("1x2, 2x1 Port %d (x2), Port %d (disabled), "
+ "Ports %d, %d (x1)", 1+off, 2+off, 3+off, 4+off);
+ break;
+ case 2:
+ msg_pdbg2("2x2 Port %d (x2), Port %d (x2), Ports "
+ "%d, %d (disabled)", 1+off, 3+off, 2+off, 4+off);
+ break;
+ case 3:
+ msg_pdbg2("1x4 Port %d (x4), Ports %d-%d (disabled)",
+ 1+off, 2+off, 4+off);
+ break;
+ }
+ msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_pchstraps45678_56(const struct ich_desc_south_strap *s)
+{
+ /* PCHSTRP4 */
+ msg_pdbg2("Intel PHY is %s.\n",
+ (s->ibex.PHYCON == 2) ? "connected" :
+ (s->ibex.PHYCON == 0) ? "disconnected" : "reserved");
+ msg_pdbg2("GbE MAC SMBus address is %sabled.\n",
+ s->ibex.GBEMAC_SMBUS_ADDR_EN ? "en" : "dis");
+ msg_pdbg2("GbE MAC SMBus address: 0x%02x\n",
+ s->ibex.GBEMAC_SMBUS_ADDR);
+ msg_pdbg2("GbE PHY SMBus address: 0x%02x\n",
+ s->ibex.GBEPHY_SMBUS_ADDR);
+
+ /* PCHSTRP5 */
+ /* PCHSTRP6 */
+ /* PCHSTRP7 */
+ msg_pdbg2("Intel ME SMBus Subsystem Vendor ID: 0x%04x\n",
+ s->ibex.MESMA2UDID_VENDOR);
+ msg_pdbg2("Intel ME SMBus Subsystem Device ID: 0x%04x\n",
+ s->ibex.MESMA2UDID_VENDOR);
+
+ /* PCHSTRP8 */
+}
+
+void prettyprint_ich_descriptor_pchstraps111213_56(const struct ich_desc_south_strap *s)
+{
+ /* PCHSTRP11 */
+ msg_pdbg2("SMLink1 GP Address is %sabled.\n",
+ s->ibex.SML1GPAEN ? "en" : "dis");
+ msg_pdbg2("SMLink1 controller General Purpose Target address: 0x%02x\n",
+ s->ibex.SML1GPA);
+ msg_pdbg2("SMLink1 I2C Target address is %sabled.\n",
+ s->ibex.SML1I2CAEN ? "en" : "dis");
+ msg_pdbg2("SMLink1 I2C Target address: 0x%02x\n",
+ s->ibex.SML1I2CA);
+
+ /* PCHSTRP12 */
+ /* PCHSTRP13 */
+}
+
+void prettyprint_ich_descriptor_straps_ibex(const struct ich_desc_south_strap *s)
+{
+ static const uint8_t dec_t209min[4] = {
+ 100,
+ 50,
+ 5,
+ 1
+ };
+
+ msg_pdbg2("--- PCH ---\n");
+
+ /* PCHSTRP0 */
+ msg_pdbg2("Chipset configuration Softstrap 2: %d\n", s->ibex.cs_ss2);
+ msg_pdbg2("Intel ME SMBus Select is %sabled.\n",
+ s->ibex.SMB_EN ? "en" : "dis");
+ msg_pdbg2("SMLink0 segment is %sabled.\n",
+ s->ibex.SML0_EN ? "en" : "dis");
+ msg_pdbg2("SMLink1 segment is %sabled.\n",
+ s->ibex.SML1_EN ? "en" : "dis");
+ msg_pdbg2("SMLink1 Frequency: %s\n",
+ (s->ibex.SML1FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("Intel ME SMBus Frequency: %s\n",
+ (s->ibex.SMB0FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("SMLink0 Frequency: %s\n",
+ (s->ibex.SML0FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("GPIO12 is used as %s.\n", s->ibex.LANPHYPC_GP12_SEL ?
+ "LAN_PHY_PWR_CTRL" : "general purpose output");
+ msg_pdbg2("Chipset configuration Softstrap 1: %d\n", s->ibex.cs_ss1);
+ msg_pdbg2("DMI RequesterID Checks are %sabled.\n",
+ s->ibex.DMI_REQID_DIS ? "en" : "dis");
+ msg_pdbg2("BIOS Boot-Block size (BBBS): %d kB.\n",
+ 1 << (6 + s->ibex.BBBS));
+
+ /* PCHSTRP1 */
+ msg_pdbg2("Chipset configuration Softstrap 3: 0x%x\n", s->ibex.cs_ss3);
+
+ /* PCHSTRP2 */
+ msg_pdbg2("ME SMBus ASD address is %sabled.\n",
+ s->ibex.MESMASDEN ? "en" : "dis");
+ msg_pdbg2("ME SMBus Controller ASD Target address: 0x%02x\n",
+ s->ibex.MESMASDA);
+ msg_pdbg2("ME SMBus I2C address is %sabled.\n",
+ s->ibex.MESMI2CEN ? "en" : "dis");
+ msg_pdbg2("ME SMBus I2C target address: 0x%02x\n",
+ s->ibex.MESMI2CA);
+
+ /* PCHSTRP3 */
+ prettyprint_ich_descriptor_pchstraps45678_56(s);
+ /* PCHSTRP9 */
+ prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 0);
+ prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 1);
+ msg_pdbg2("PCIe Lane Reversal 1: PCIe Lanes 0-3 are %sreserved.\n",
+ s->ibex.PCIELR1 ? "" : "not ");
+ msg_pdbg2("PCIe Lane Reversal 2: PCIe Lanes 4-7 are %sreserved.\n",
+ s->ibex.PCIELR2 ? "" : "not ");
+ msg_pdbg2("DMI Lane Reversal: DMI Lanes 0-3 are %sreserved.\n",
+ s->ibex.DMILR ? "" : "not ");
+ msg_pdbg2("Default PHY PCIe Port is %d.\n", s->ibex.PHY_PCIEPORTSEL+1);
+ msg_pdbg2("Integrated MAC/PHY communication over PCIe is %sabled.\n",
+ s->ibex.PHY_PCIE_EN ? "en" : "dis");
+
+ /* PCHSTRP10 */
+ msg_pdbg2("Management Engine will boot from %sflash.\n",
+ s->ibex.ME_BOOT_FLASH ? "" : "ROM, then ");
+ msg_pdbg2("Chipset configuration Softstrap 5: %d\n", s->ibex.cs_ss5);
+ msg_pdbg2("Virtualization Engine Enable 1 is %sabled.\n",
+ s->ibex.VE_EN ? "en" : "dis");
+ msg_pdbg2("ME Memory-attached Debug Display Device is %sabled.\n",
+ s->ibex.MMDDE ? "en" : "dis");
+ msg_pdbg2("ME Memory-attached Debug Display Device address: 0x%02x\n",
+ s->ibex.MMADDR);
+ msg_pdbg2("Chipset configuration Softstrap 7: %d\n", s->ibex.cs_ss7);
+ msg_pdbg2("Integrated Clocking Configuration is %d.\n",
+ (s->ibex.ICC_SEL == 7) ? 0 : s->ibex.ICC_SEL);
+ msg_pdbg2("PCH Signal CL_RST1# does %sassert when Intel ME performs a "
+ "reset.\n", s->ibex.MER_CL1 ? "" : "not ");
+
+ prettyprint_ich_descriptor_pchstraps111213_56(s);
+
+ /* PCHSTRP14 */
+ msg_pdbg2("Virtualization Engine Enable 2 is %sabled.\n",
+ s->ibex.VE_EN2 ? "en" : "dis");
+ msg_pdbg2("Virtualization Engine will boot from %sflash.\n",
+ s->ibex.VE_BOOT_FLASH ? "" : "ROM, then ");
+ msg_pdbg2("Braidwood SSD functionality is %sabled.\n",
+ s->ibex.BW_SSD ? "en" : "dis");
+ msg_pdbg2("Braidwood NVMHCI functionality is %sabled.\n",
+ s->ibex.NVMHCI_EN ? "en" : "dis");
+
+ /* PCHSTRP15 */
+ msg_pdbg2("Chipset configuration Softstrap 6: %d\n", s->ibex.cs_ss6);
+ msg_pdbg2("Integrated wired LAN Solution is %sabled.\n",
+ s->ibex.IWL_EN ? "en" : "dis");
+ msg_pdbg2("t209 min Timing: %d ms\n",
+ dec_t209min[s->ibex.t209min]);
+ msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_straps_cougar(const struct ich_desc_south_strap *s)
+{
+ msg_pdbg2("--- PCH ---\n");
+
+ /* PCHSTRP0 */
+ msg_pdbg2("Chipset configuration Softstrap 1: %d\n", s->cougar.cs_ss1);
+ msg_pdbg2("Intel ME SMBus Select is %sabled.\n",
+ s->ibex.SMB_EN ? "en" : "dis");
+ msg_pdbg2("SMLink0 segment is %sabled.\n",
+ s->ibex.SML0_EN ? "en" : "dis");
+ msg_pdbg2("SMLink1 segment is %sabled.\n",
+ s->ibex.SML1_EN ? "en" : "dis");
+ msg_pdbg2("SMLink1 Frequency: %s\n",
+ (s->ibex.SML1FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("Intel ME SMBus Frequency: %s\n",
+ (s->ibex.SMB0FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("SMLink0 Frequency: %s\n",
+ (s->ibex.SML0FRQ == 1) ? "100 kHz" : "reserved");
+ msg_pdbg2("GPIO12 is used as %s.\n", s->ibex.LANPHYPC_GP12_SEL ?
+ "LAN_PHY_PWR_CTRL" : "general purpose output");
+ msg_pdbg2("LinkSec is %sabled.\n",
+ s->cougar.LINKSEC_DIS ? "en" : "dis");
+ msg_pdbg2("DMI RequesterID Checks are %sabled.\n",
+ s->ibex.DMI_REQID_DIS ? "en" : "dis");
+ msg_pdbg2("BIOS Boot-Block size (BBBS): %d kB.\n",
+ 1 << (6 + s->ibex.BBBS));
+
+ /* PCHSTRP1 */
+ msg_pdbg2("Chipset configuration Softstrap 3: 0x%x\n", s->ibex.cs_ss3);
+ msg_pdbg2("Chipset configuration Softstrap 2: 0x%x\n", s->ibex.cs_ss2);
+
+ /* PCHSTRP2 */
+ msg_pdbg2("ME SMBus ASD address is %sabled.\n",
+ s->ibex.MESMASDEN ? "en" : "dis");
+ msg_pdbg2("ME SMBus Controller ASD Target address: 0x%02x\n",
+ s->ibex.MESMASDA);
+ msg_pdbg2("ME SMBus MCTP Address is %sabled.\n",
+ s->cougar.MESMMCTPAEN ? "en" : "dis");
+ msg_pdbg2("ME SMBus MCTP target address: 0x%02x\n",
+ s->cougar.MESMMCTPA);
+ msg_pdbg2("ME SMBus I2C address is %sabled.\n",
+ s->ibex.MESMI2CEN ? "en" : "dis");
+ msg_pdbg2("ME SMBus I2C target address: 0x%02x\n",
+ s->ibex.MESMI2CA);
+
+ /* PCHSTRP3 */
+ prettyprint_ich_descriptor_pchstraps45678_56(s);
+ /* PCHSTRP9 */
+ prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 0);
+ prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 1);
+ msg_pdbg2("PCIe Lane Reversal 1: PCIe Lanes 0-3 are %sreserved.\n",
+ s->ibex.PCIELR1 ? "" : "not ");
+ msg_pdbg2("PCIe Lane Reversal 2: PCIe Lanes 4-7 are %sreserved.\n",
+ s->ibex.PCIELR2 ? "" : "not ");
+ msg_pdbg2("DMI Lane Reversal: DMI Lanes 0-3 are %sreserved.\n",
+ s->ibex.DMILR ? "" : "not ");
+ msg_pdbg2("ME Debug status writes over SMBUS are %sabled.\n",
+ s->cougar.MDSMBE_EN ? "en" : "dis");
+ msg_pdbg2("ME Debug SMBus Emergency Mode address: 0x%02x (raw)\n",
+ s->cougar.MDSMBE_ADD);
+ msg_pdbg2("Default PHY PCIe Port is %d.\n", s->ibex.PHY_PCIEPORTSEL+1);
+ msg_pdbg2("Integrated MAC/PHY communication over PCIe is %sabled.\n",
+ s->ibex.PHY_PCIE_EN ? "en" : "dis");
+ msg_pdbg2("PCIe ports Subtractive Decode Agent is %sabled.\n",
+ s->cougar.SUB_DECODE_EN ? "en" : "dis");
+ msg_pdbg2("GPIO74 is used as %s.\n", s->cougar.PCHHOT_SML1ALERT_SEL ?
+ "PCHHOT#" : "SML1ALERT#");
+
+ /* PCHSTRP10 */
+ msg_pdbg2("Management Engine will boot from %sflash.\n",
+ s->ibex.ME_BOOT_FLASH ? "" : "ROM, then ");
+
+ msg_pdbg2("ME Debug SMBus Emergency Mode is %sabled.\n",
+ s->cougar.MDSMBE_EN ? "en" : "dis");
+ msg_pdbg2("ME Debug SMBus Emergency Mode Address: 0x%02x\n",
+ s->cougar.MDSMBE_ADD);
+
+ msg_pdbg2("Integrated Clocking Configuration used: %d\n",
+ s->cougar.ICC_SEL);
+ msg_pdbg2("PCH Signal CL_RST1# does %sassert when Intel ME performs a reset.\n",
+ s->ibex.MER_CL1 ? "" : "not ");
+ msg_pdbg2("ICC Profile is selected by %s.\n",
+ s->cougar.ICC_PRO_SEL ? "Softstraps" : "BIOS");
+ msg_pdbg2("Deep SX is %ssupported on the platform.\n",
+ s->cougar.Deep_SX_EN ? "not " : "");
+ msg_pdbg2("ME Debug LAN Emergency Mode is %sabled.\n",
+ s->cougar.ME_DBG_LAN ? "en" : "dis");
+
+ prettyprint_ich_descriptor_pchstraps111213_56(s);
+
+ /* PCHSTRP14 */
+ /* PCHSTRP15 */
+ msg_pdbg2("Chipset configuration Softstrap 6: %d\n", s->cougar.cs_ss6);
+ msg_pdbg2("Integrated wired LAN is %sabled.\n",
+ s->cougar.IWL_EN ? "en" : "dis");
+ msg_pdbg2("Chipset configuration Softstrap 5: %d\n", s->cougar.cs_ss5);
+ msg_pdbg2("SMLink1 provides temperature from %s.\n",
+ s->cougar.SMLINK1_THERM_SEL ? "PCH only" : "the CPU, PCH and DIMMs");
+ msg_pdbg2("GPIO29 is used as %s.\n", s->cougar.SLP_LAN_GP29_SEL ?
+ "general purpose output" : "SLP_LAN#");
+
+ /* PCHSTRP16 */
+ /* PCHSTRP17 */
+ msg_pdbg2("Integrated Clock: %s Clock Mode\n",
+ s->cougar.ICML ? "Buffered Through" : "Full Integrated");
+ msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_straps(enum ich_chipset cs, const struct ich_descriptors *desc)
+{
+ unsigned int i, max_count;
+ msg_pdbg2("=== Softstraps ===\n");
+
+ if (sizeof(desc->north.STRPs) / 4 + 1 < desc->content.MSL) {
+ max_count = sizeof(desc->north.STRPs) / 4 + 1;
+ msg_pdbg2("MSL (%u) is greater than the current maximum of %u entries.\n",
+ desc->content.MSL, max_count + 1);
+ msg_pdbg2("Only the first %u entries will be printed.\n", max_count);
+ } else
+ max_count = desc->content.MSL;
+
+ msg_pdbg2("--- North/MCH/PROC (%d entries) ---\n", max_count);
+ for (i = 0; i < max_count; i++)
+ msg_pdbg2("STRP%-2d = 0x%08x\n", i, desc->north.STRPs[i]);
+ msg_pdbg2("\n");
+
+ if (sizeof(desc->south.STRPs) / 4 < desc->content.ISL) {
+ max_count = sizeof(desc->south.STRPs) / 4;
+ msg_pdbg2("ISL (%u) is greater than the current maximum of %u entries.\n",
+ desc->content.ISL, max_count);
+ msg_pdbg2("Only the first %u entries will be printed.\n", max_count);
+ } else
+ max_count = desc->content.ISL;
+
+ msg_pdbg2("--- South/ICH/PCH (%d entries) ---\n", max_count);
+ for (i = 0; i < max_count; i++)
+ msg_pdbg2("STRP%-2d = 0x%08x\n", i, desc->south.STRPs[i]);
+ msg_pdbg2("\n");
+
+ switch (cs) {
+ case CHIPSET_ICH8:
+ if (sizeof(desc->north.ich8) / 4 != desc->content.MSL)
+ msg_pdbg2("Detailed North/MCH/PROC information is "
+ "probably not reliable, printing anyway.\n");
+ if (sizeof(desc->south.ich8) / 4 != desc->content.ISL)
+ msg_pdbg2("Detailed South/ICH/PCH information is "
+ "probably not reliable, printing anyway.\n");
+ prettyprint_ich_descriptor_straps_ich8(desc);
+ break;
+ case CHIPSET_5_SERIES_IBEX_PEAK:
+ /* PCH straps only. PROCSTRPs are unknown. */
+ if (sizeof(desc->south.ibex) / 4 != desc->content.ISL)
+ msg_pdbg2("Detailed South/ICH/PCH information is "
+ "probably not reliable, printing anyway.\n");
+ prettyprint_ich_descriptor_straps_ibex(&desc->south);
+ break;
+ case CHIPSET_6_SERIES_COUGAR_POINT:
+ /* PCH straps only. PROCSTRP0 is "reserved". */
+ if (sizeof(desc->south.cougar) / 4 != desc->content.ISL)
+ msg_pdbg2("Detailed South/ICH/PCH information is "
+ "probably not reliable, printing anyway.\n");
+ prettyprint_ich_descriptor_straps_cougar(&desc->south);
+ break;
+ case CHIPSET_ICH_UNKNOWN:
+ break;
+ default:
+ msg_pdbg2("The meaning of the descriptor straps are unknown yet.\n\n");
+ break;
+ }
+}
+
+void prettyprint_rdid(uint32_t reg_val)
+{
+ uint8_t mid = reg_val & 0xFF;
+ uint16_t did = ((reg_val >> 16) & 0xFF) | (reg_val & 0xFF00);
+ msg_pdbg2("Manufacturer ID 0x%02x, Device ID 0x%04x\n", mid, did);
+}
+
+void prettyprint_ich_descriptor_upper_map(const struct ich_desc_upper_map *umap)
+{
+ int i;
+ msg_pdbg2("=== Upper Map Section ===\n");
+ msg_pdbg2("FLUMAP1 0x%08x\n", umap->FLUMAP1);
+ msg_pdbg2("\n");
+
+ msg_pdbg2("--- Details ---\n");
+ msg_pdbg2("VTL (length in DWORDS) = %d\n", umap->VTL);
+ msg_pdbg2("VTBA (base address) = 0x%6.6x\n", getVTBA(umap));
+ msg_pdbg2("\n");
+
+ msg_pdbg2("VSCC Table: %d entries\n", umap->VTL/2);
+ for (i = 0; i < umap->VTL/2; i++) {
+ uint32_t jid = umap->vscc_table[i].JID;
+ uint32_t vscc = umap->vscc_table[i].VSCC;
+ msg_pdbg2(" JID%d = 0x%08x\n", i, jid);
+ msg_pdbg2(" VSCC%d = 0x%08x\n", i, vscc);
+ msg_pdbg2(" "); /* indention */
+ prettyprint_rdid(jid);
+ msg_pdbg2(" "); /* indention */
+ prettyprint_ich_reg_vscc(vscc, 0, false);
+ }
+ msg_pdbg2("\n");
+}
+
+/* len is the length of dump in bytes */
+int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struct ich_descriptors *desc)
+{
+ unsigned int i, max_count;
+ uint8_t pch_bug_offset = 0;
+
+ if (dump == NULL || desc == NULL)
+ return ICH_RET_PARAM;
+
+ if (dump[0] != DESCRIPTOR_MODE_SIGNATURE) {
+ if (dump[4] == DESCRIPTOR_MODE_SIGNATURE)
+ pch_bug_offset = 4;
+ else
+ return ICH_RET_ERR;
+ }
+
+ /* map */
+ if (len < (4 + pch_bug_offset) * 4 - 1)
+ return ICH_RET_OOB;
+ desc->content.FLVALSIG = dump[0 + pch_bug_offset];
+ desc->content.FLMAP0 = dump[1 + pch_bug_offset];
+ desc->content.FLMAP1 = dump[2 + pch_bug_offset];
+ desc->content.FLMAP2 = dump[3 + pch_bug_offset];
+
+ /* component */
+ if (len < (getFCBA(&desc->content) + 3 * 4 - 1))
+ return ICH_RET_OOB;
+ desc->component.FLCOMP = dump[(getFCBA(&desc->content) >> 2) + 0];
+ desc->component.FLILL = dump[(getFCBA(&desc->content) >> 2) + 1];
+ desc->component.FLPB = dump[(getFCBA(&desc->content) >> 2) + 2];
+
+ /* region */
+ if (len < (getFRBA(&desc->content) + 5 * 4 - 1))
+ return ICH_RET_OOB;
+ desc->region.FLREGs[0] = dump[(getFRBA(&desc->content) >> 2) + 0];
+ desc->region.FLREGs[1] = dump[(getFRBA(&desc->content) >> 2) + 1];
+ desc->region.FLREGs[2] = dump[(getFRBA(&desc->content) >> 2) + 2];
+ desc->region.FLREGs[3] = dump[(getFRBA(&desc->content) >> 2) + 3];
+ desc->region.FLREGs[4] = dump[(getFRBA(&desc->content) >> 2) + 4];
+
+ /* master */
+ if (len < (getFMBA(&desc->content) + 3 * 4 - 1))
+ return ICH_RET_OOB;
+ desc->master.FLMSTR1 = dump[(getFMBA(&desc->content) >> 2) + 0];
+ desc->master.FLMSTR2 = dump[(getFMBA(&desc->content) >> 2) + 1];
+ desc->master.FLMSTR3 = dump[(getFMBA(&desc->content) >> 2) + 2];
+
+ /* upper map */
+ desc->upper.FLUMAP1 = dump[(UPPER_MAP_OFFSET >> 2) + 0];
+
+ /* VTL is 8 bits long. Quote from the Ibex Peak SPI programming guide:
+ * "Identifies the 1s based number of DWORDS contained in the VSCC
+ * Table. Each SPI component entry in the table is 2 DWORDS long." So
+ * the maximum of 255 gives us 127.5 SPI components(!?) 8 bytes each. A
+ * check ensures that the maximum offset actually accessed is available.
+ */
+ if (len < (getVTBA(&desc->upper) + (desc->upper.VTL / 2 * 8) - 1))
+ return ICH_RET_OOB;
+
+ for (i = 0; i < desc->upper.VTL/2; i++) {
+ desc->upper.vscc_table[i].JID = dump[(getVTBA(&desc->upper) >> 2) + i * 2 + 0];
+ desc->upper.vscc_table[i].VSCC = dump[(getVTBA(&desc->upper) >> 2) + i * 2 + 1];
+ }
+
+ /* MCH/PROC (aka. North) straps */
+ if (len < getFMSBA(&desc->content) + desc->content.MSL * 4)
+ return ICH_RET_OOB;
+
+ /* limit the range to be written */
+ max_count = min(sizeof(desc->north.STRPs) / 4, desc->content.MSL);
+ for (i = 0; i < max_count; i++)
+ desc->north.STRPs[i] = dump[(getFMSBA(&desc->content) >> 2) + i];
+
+ /* ICH/PCH (aka. South) straps */
+ if (len < getFISBA(&desc->content) + desc->content.ISL * 4)
+ return ICH_RET_OOB;
+
+ /* limit the range to be written */
+ max_count = min(sizeof(desc->south.STRPs) / 4, desc->content.ISL);
+ for (i = 0; i < max_count; i++)
+ desc->south.STRPs[i] = dump[(getFISBA(&desc->content) >> 2) + i];
+
+ return ICH_RET_OK;
+}
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
+/** Returns the integer representation of the component density with index
+\em idx in bytes or -1 if the correct size can not be determined. */
+int getFCBA_component_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx)
+{
+ if (idx > 1) {
+ msg_perr("Only ICH SPI component index 0 or 1 are supported yet.\n");
+ return -1;
+ }
+
+ if (desc->content.NC == 0 && idx > 0)
+ return 0;
+
+ uint8_t size_enc;
+ uint8_t size_max;
+
+ switch (cs) {
+ case CHIPSET_ICH8:
+ case CHIPSET_ICH9:
+ case CHIPSET_ICH10:
+ case CHIPSET_5_SERIES_IBEX_PEAK:
+ case CHIPSET_6_SERIES_COUGAR_POINT:
+ case CHIPSET_7_SERIES_PANTHER_POINT:
+ case CHIPSET_BAYTRAIL:
+ if (idx == 0) {
+ size_enc = desc->component.dens_old.comp1_density;
+ } else {
+ size_enc = desc->component.dens_old.comp2_density;
+ }
+ size_max = 5;
+ break;
+ case CHIPSET_8_SERIES_LYNX_POINT:
+ case CHIPSET_8_SERIES_LYNX_POINT_LP:
+ case CHIPSET_8_SERIES_WELLSBURG:
+ case CHIPSET_9_SERIES_WILDCAT_POINT:
+ if (idx == 0) {
+ size_enc = desc->component.dens_new.comp1_density;
+ } else {
+ size_enc = desc->component.dens_new.comp2_density;
+ }
+ size_max = 7;
+ break;
+ case CHIPSET_ICH_UNKNOWN:
+ default:
+ msg_pwarn("Density encoding is unknown on this chipset.\n");
+ return -1;
+ }
+
+ if (size_enc > size_max) {
+ msg_perr("Density of ICH SPI component with index %d is invalid.\n"
+ "Encoded density is 0x%x while maximum allowed is 0x%x.\n",
+ idx, size_enc, size_max);
+ return -1;
+ }
+
+ return (1 << (19 + size_enc));
+}
+
+static uint32_t read_descriptor_reg(uint8_t section, uint16_t offset, void *spibar)
+{
+ uint32_t control = 0;
+ control |= (section << FDOC_FDSS_OFF) & FDOC_FDSS;
+ control |= (offset << FDOC_FDSI_OFF) & FDOC_FDSI;
+ mmio_le_writel(control, spibar + ICH9_REG_FDOC);
+ return mmio_le_readl(spibar + ICH9_REG_FDOD);
+}
+
+int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc)
+{
+ uint8_t i;
+ uint8_t nr;
+ struct ich_desc_region *r = &desc->region;
+
+ /* Test if bit-fields are working as expected.
+ * FIXME: Replace this with dynamic bitfield fixup
+ */
+ for (i = 0; i < 4; i++)
+ desc->region.FLREGs[i] = 0x5A << (i * 8);
+ if (r->reg0_base != 0x005A || r->reg0_limit != 0x0000 ||
+ r->reg1_base != 0x1A00 || r->reg1_limit != 0x0000 ||
+ r->reg2_base != 0x0000 || r->reg2_limit != 0x005A ||
+ r->reg3_base != 0x0000 || r->reg3_limit != 0x1A00) {
+ msg_pdbg("The combination of compiler and CPU architecture used"
+ "does not lay out bit-fields as expected, sorry.\n");
+ msg_pspew("r->reg0_base = 0x%04X (0x005A)\n", r->reg0_base);
+ msg_pspew("r->reg0_limit = 0x%04X (0x0000)\n", r->reg0_limit);
+ msg_pspew("r->reg1_base = 0x%04X (0x1A00)\n", r->reg1_base);
+ msg_pspew("r->reg1_limit = 0x%04X (0x0000)\n", r->reg1_limit);
+ msg_pspew("r->reg2_base = 0x%04X (0x0000)\n", r->reg2_base);
+ msg_pspew("r->reg2_limit = 0x%04X (0x005A)\n", r->reg2_limit);
+ msg_pspew("r->reg3_base = 0x%04X (0x0000)\n", r->reg3_base);
+ msg_pspew("r->reg3_limit = 0x%04X (0x1A00)\n", r->reg3_limit);
+ return ICH_RET_ERR;
+ }
+
+ msg_pdbg2("Reading flash descriptors mapped by the chipset via FDOC/FDOD...");
+ /* content section */
+ desc->content.FLVALSIG = read_descriptor_reg(0, 0, spibar);
+ desc->content.FLMAP0 = read_descriptor_reg(0, 1, spibar);
+ desc->content.FLMAP1 = read_descriptor_reg(0, 2, spibar);
+ desc->content.FLMAP2 = read_descriptor_reg(0, 3, spibar);
+
+ /* component section */
+ desc->component.FLCOMP = read_descriptor_reg(1, 0, spibar);
+ desc->component.FLILL = read_descriptor_reg(1, 1, spibar);
+ desc->component.FLPB = read_descriptor_reg(1, 2, spibar);
+
+ /* region section */
+ nr = desc->content.NR + 1;
+ if (nr > 5) {
+ msg_pdbg2("%s: number of regions too high (%d) - failed\n",
+ __func__, nr);
+ return ICH_RET_ERR;
+ }
+ for (i = 0; i < 5; i++)
+ desc->region.FLREGs[i] = read_descriptor_reg(2, i, spibar);
+
+ /* master section */
+ desc->master.FLMSTR1 = read_descriptor_reg(3, 0, spibar);
+ desc->master.FLMSTR2 = read_descriptor_reg(3, 1, spibar);
+ desc->master.FLMSTR3 = read_descriptor_reg(3, 2, spibar);
+
+ /* Accessing the strap section via FDOC/D is only possible on ICH8 and
+ * reading the upper map is impossible on all chipsets, so don't bother.
+ */
+
+ msg_pdbg2(" done.\n");
+ return ICH_RET_OK;
+}
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/ich_descriptors.h b/ich_descriptors.h
new file mode 100644
index 0000000..2c21598
--- /dev/null
+++ b/ich_descriptors.h
@@ -0,0 +1,598 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (c) 2010 Matthias Wenzel <bios at mazzoo dot de>
+ * Copyright (c) 2011 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+#ifndef __ICH_DESCRIPTORS_H__
+#define __ICH_DESCRIPTORS_H__ 1
+
+#include <stdint.h>
+#include "programmer.h" /* for enum ich_chipset */
+
+/* FIXME: Replace with generic return codes */
+#define ICH_RET_OK 0
+#define ICH_RET_ERR -1
+#define ICH_RET_WARN -2
+#define ICH_RET_PARAM -3
+#define ICH_RET_OOB -4
+
+#define ICH9_REG_FDOC 0xB0 /* 32 Bits Flash Descriptor Observability Control */
+ /* 0-1: reserved */
+#define FDOC_FDSI_OFF 2 /* 2-11: Flash Descriptor Section Index */
+#define FDOC_FDSI (0x3f << FDOC_FDSI_OFF)
+#define FDOC_FDSS_OFF 12 /* 12-14: Flash Descriptor Section Select */
+#define FDOC_FDSS (0x3 << FDOC_FDSS_OFF)
+ /* 15-31: reserved */
+
+#define ICH9_REG_FDOD 0xB4 /* 32 Bits Flash Descriptor Observability Data */
+
+/* Field locations and semantics for LVSCC, UVSCC and related words in the flash
+ * descriptor are equal therefore they all share the same macros below. */
+#define VSCC_BES_OFF 0 /* 0-1: Block/Sector Erase Size */
+#define VSCC_BES (0x3 << VSCC_BES_OFF)
+#define VSCC_WG_OFF 2 /* 2: Write Granularity */
+#define VSCC_WG (0x1 << VSCC_WG_OFF)
+#define VSCC_WSR_OFF 3 /* 3: Write Status Required */
+#define VSCC_WSR (0x1 << VSCC_WSR_OFF)
+#define VSCC_WEWS_OFF 4 /* 4: Write Enable on Write Status */
+#define VSCC_WEWS (0x1 << VSCC_WEWS_OFF)
+ /* 5-7: reserved */
+#define VSCC_EO_OFF 8 /* 8-15: Erase Opcode */
+#define VSCC_EO (0xff << VSCC_EO_OFF)
+ /* 16-22: reserved */
+#define VSCC_VCL_OFF 23 /* 23: Vendor Component Lock */
+#define VSCC_VCL (0x1 << VSCC_VCL_OFF)
+ /* 24-31: reserved */
+
+#define ICH_FREG_BASE(flreg) (((flreg) << 12) & 0x01fff000)
+#define ICH_FREG_LIMIT(flreg) (((flreg) >> 4) & 0x01fff000)
+
+void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity, bool print_vcl);
+
+struct ich_desc_content {
+ uint32_t FLVALSIG; /* 0x00 */
+ union { /* 0x04 */
+ uint32_t FLMAP0;
+ struct {
+ uint32_t FCBA :8, /* Flash Component Base Address */
+ NC :2, /* Number Of Components */
+ :6,
+ FRBA :8, /* Flash Region Base Address */
+ NR :3, /* Number Of Regions */
+ :5;
+ };
+ };
+ union { /* 0x08 */
+ uint32_t FLMAP1;
+ struct {
+ uint32_t FMBA :8, /* Flash Master Base Address */
+ NM :3, /* Number Of Masters */
+ :5,
+ FISBA :8, /* Flash ICH Strap Base Address */
+ ISL :8; /* ICH Strap Length */
+ };
+ };
+ union { /* 0x0c */
+ uint32_t FLMAP2;
+ struct {
+ uint32_t FMSBA :8, /* Flash (G)MCH Strap Base Addr. */
+ MSL :8, /* MCH Strap Length */
+ :16;
+ };
+ };
+};
+
+struct ich_desc_component {
+ union { /* 0x00 */
+ uint32_t FLCOMP; /* Flash Components Register */
+ /* FLCOMP encoding on various generations:
+ *
+ * Chipset/Generation max_speed dual_output density
+ * [MHz] bits max. bits
+ * ICH8: 33 N/A 5 0:2, 3:5
+ * ICH9: 33 N/A 5 0:2, 3:5
+ * ICH10: 33 N/A 5 0:2, 3:5
+ * Ibex Peak/5: 50 N/A 5 0:2, 3:5
+ * Cougar Point/6: 50 30 5 0:2, 3:5
+ * Patsburg: 50 30 5 0:2, 3:5
+ * Panther Point/7 50 30 5 0:2, 3:5
+ * Lynx Point/8: 50 30 7 0:3, 4:7
+ * Wildcat Point/9: 50 ?? (multi I/O) ? ?:?, ?:?
+ */
+ struct {
+ uint32_t :17,
+ freq_read :3,
+ fastread :1,
+ freq_fastread :3,
+ freq_write :3,
+ freq_read_id :3,
+ dual_output :1, /* new since Cougar Point/6 */
+ :1;
+ } modes;
+ struct {
+ uint32_t comp1_density :3,
+ comp2_density :3,
+ :26;
+ } dens_old;
+ struct {
+ uint32_t comp1_density :4, /* new since Lynx Point/8 */
+ comp2_density :4,
+ :24;
+ } dens_new;
+ };
+ union { /* 0x04 */
+ uint32_t FLILL; /* Flash Invalid Instructions Register */
+ struct {
+ uint32_t invalid_instr0 :8,
+ invalid_instr1 :8,
+ invalid_instr2 :8,
+ invalid_instr3 :8;
+ };
+ };
+ union { /* 0x08 */
+ uint32_t FLPB; /* Flash Partition Boundary Register */
+ struct {
+ uint32_t FPBA :13, /* Flash Partition Boundary Addr */
+ :19;
+ };
+ };
+};
+
+struct ich_desc_region {
+ union {
+ uint32_t FLREGs[5];
+ struct {
+ struct { /* FLREG0 Flash Descriptor */
+ uint32_t reg0_base :13,
+ :3,
+ reg0_limit :13,
+ :3;
+ };
+ struct { /* FLREG1 BIOS */
+ uint32_t reg1_base :13,
+ :3,
+ reg1_limit :13,
+ :3;
+ };
+ struct { /* FLREG2 ME */
+ uint32_t reg2_base :13,
+ :3,
+ reg2_limit :13,
+ :3;
+ };
+ struct { /* FLREG3 GbE */
+ uint32_t reg3_base :13,
+ :3,
+ reg3_limit :13,
+ :3;
+ };
+ struct { /* FLREG4 Platform */
+ uint32_t reg4_base :13,
+ :3,
+ reg4_limit :13,
+ :3;
+ };
+ };
+ };
+};
+
+struct ich_desc_master {
+ union {
+ uint32_t FLMSTR1;
+ struct {
+ uint32_t BIOS_req_ID :16,
+ BIOS_descr_r :1,
+ BIOS_BIOS_r :1,
+ BIOS_ME_r :1,
+ BIOS_GbE_r :1,
+ BIOS_plat_r :1,
+ :3,
+ BIOS_descr_w :1,
+ BIOS_BIOS_w :1,
+ BIOS_ME_w :1,
+ BIOS_GbE_w :1,
+ BIOS_plat_w :1,
+ :3;
+ };
+ };
+ union {
+ uint32_t FLMSTR2;
+ struct {
+ uint32_t ME_req_ID :16,
+ ME_descr_r :1,
+ ME_BIOS_r :1,
+ ME_ME_r :1,
+ ME_GbE_r :1,
+ ME_plat_r :1,
+ :3,
+ ME_descr_w :1,
+ ME_BIOS_w :1,
+ ME_ME_w :1,
+ ME_GbE_w :1,
+ ME_plat_w :1,
+ :3;
+ };
+ };
+ union {
+ uint32_t FLMSTR3;
+ struct {
+ uint32_t GbE_req_ID :16,
+ GbE_descr_r :1,
+ GbE_BIOS_r :1,
+ GbE_ME_r :1,
+ GbE_GbE_r :1,
+ GbE_plat_r :1,
+ :3,
+ GbE_descr_w :1,
+ GbE_BIOS_w :1,
+ GbE_ME_w :1,
+ GbE_GbE_w :1,
+ GbE_plat_w :1,
+ :3;
+ };
+ };
+};
+
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+struct ich_desc_north_strap {
+ union {
+ uint32_t STRPs[1]; /* current maximum: ich8 */
+ struct { /* ich8 */
+ struct { /* STRP2 (in the datasheet) */
+ uint32_t MDB :1,
+ :31;
+ };
+ } ich8;
+ };
+};
+
+struct ich_desc_south_strap {
+ union {
+ uint32_t STRPs[18]; /* current maximum: cougar point */
+ struct { /* ich8 */
+ struct { /* STRP1 */
+ uint32_t ME_DISABLE :1,
+ :6,
+ TCOMODE :1,
+ ASD :7,
+ BMCMODE :1,
+ :3,
+ GLAN_PCIE_SEL :1,
+ GPIO12_SEL :2,
+ SPICS1_LANPHYPC_SEL :1,
+ MESM2SEL :1,
+ :1,
+ ASD2 :7;
+ };
+ } ich8;
+ struct { /* ibex peak */
+ struct { /* STRP0 */
+ uint32_t :1,
+ cs_ss2 :1,
+ :5,
+ SMB_EN :1,
+ SML0_EN :1,
+ SML1_EN :1,
+ SML1FRQ :2,
+ SMB0FRQ :2,
+ SML0FRQ :2,
+ :4,
+ LANPHYPC_GP12_SEL :1,
+ cs_ss1 :1,
+ :2,
+ DMI_REQID_DIS :1,
+ :4,
+ BBBS :2,
+ :1;
+ };
+ struct { /* STRP1 */
+ uint32_t cs_ss3 :4,
+ :28;
+ };
+ struct { /* STRP2 */
+ uint32_t :8,
+ MESMASDEN :1,
+ MESMASDA :7,
+ :8,
+ MESMI2CEN :1,
+ MESMI2CA :7;
+ };
+ struct { /* STRP3 */
+ uint32_t :32;
+ };
+ struct { /* STRP4 */
+ uint32_t PHYCON :2,
+ :6,
+ GBEMAC_SMBUS_ADDR_EN :1,
+ GBEMAC_SMBUS_ADDR :7,
+ :1,
+ GBEPHY_SMBUS_ADDR :7,
+ :8;
+ };
+ struct { /* STRP5 */
+ uint32_t :32;
+ };
+ struct { /* STRP6 */
+ uint32_t :32;
+ };
+ struct { /* STRP7 */
+ uint32_t MESMA2UDID_VENDOR :16,
+ MESMA2UDID_DEVICE :16;
+ };
+ struct { /* STRP8 */
+ uint32_t :32;
+ };
+ struct { /* STRP9 */
+ uint32_t PCIEPCS1 :2,
+ PCIEPCS2 :2,
+ PCIELR1 :1,
+ PCIELR2 :1,
+ DMILR :1,
+ :1,
+ PHY_PCIEPORTSEL :3,
+ PHY_PCIE_EN :1,
+ :20;
+ };
+ struct { /* STRP10 */
+ uint32_t :1,
+ ME_BOOT_FLASH :1,
+ cs_ss5 :1,
+ VE_EN :1,
+ :4,
+ MMDDE :1,
+ MMADDR :7,
+ cs_ss7 :1,
+ :1,
+ ICC_SEL :3,
+ MER_CL1 :1,
+ :10;
+ };
+ struct { /* STRP11 */
+ uint32_t SML1GPAEN :1,
+ SML1GPA :7,
+ :16,
+ SML1I2CAEN :1,
+ SML1I2CA :7;
+ };
+ struct { /* STRP12 */
+ uint32_t :32;
+ };
+ struct { /* STRP13 */
+ uint32_t :32;
+ };
+ struct { /* STRP14 */
+ uint32_t :8,
+ VE_EN2 :1,
+ :5,
+ VE_BOOT_FLASH :1,
+ :1,
+ BW_SSD :1,
+ NVMHCI_EN :1,
+ :14;
+ };
+ struct { /* STRP15 */
+ uint32_t :3,
+ cs_ss6 :2,
+ :1,
+ IWL_EN :1,
+ :1,
+ t209min :2,
+ :22;
+ };
+ } ibex;
+ struct { /* cougar point */
+ struct { /* STRP0 */
+ uint32_t :1,
+ cs_ss1 :1,
+ :5,
+ SMB_EN :1,
+ SML0_EN :1,
+ SML1_EN :1,
+ SML1FRQ :2,
+ SMB0FRQ :2,
+ SML0FRQ :2,
+ :4,
+ LANPHYPC_GP12_SEL :1,
+ LINKSEC_DIS :1,
+ :2,
+ DMI_REQID_DIS :1,
+ :4,
+ BBBS :2,
+ :1;
+ };
+ struct { /* STRP1 */
+ uint32_t cs_ss3 :4,
+ :4,
+ cs_ss2 :1,
+ :28;
+ };
+ struct { /* STRP2 */
+ uint32_t :8,
+ MESMASDEN :1,
+ MESMASDA :7,
+ MESMMCTPAEN :1,
+ MESMMCTPA :7,
+ MESMI2CEN :1,
+ MESMI2CA :7;
+ };
+ struct { /* STRP3 */
+ uint32_t :32;
+ };
+ struct { /* STRP4 */
+ uint32_t PHYCON :2,
+ :6,
+ GBEMAC_SMBUS_ADDR_EN :1,
+ GBEMAC_SMBUS_ADDR :7,
+ :1,
+ GBEPHY_SMBUS_ADDR :7,
+ :8;
+ };
+ struct { /* STRP5 */
+ uint32_t :32;
+ };
+ struct { /* STRP6 */
+ uint32_t :32;
+ };
+ struct { /* STRP7 */
+ uint32_t MESMA2UDID_VENDOR :16,
+ MESMA2UDID_DEVICE :16;
+ };
+ struct { /* STRP8 */
+ uint32_t :32;
+ };
+ struct { /* STRP9 */
+ uint32_t PCIEPCS1 :2,
+ PCIEPCS2 :2,
+ PCIELR1 :1,
+ PCIELR2 :1,
+ DMILR :1,
+ cs_ss4 :1,
+ PHY_PCIEPORTSEL :3,
+ PHY_PCIE_EN :1,
+ :2,
+ SUB_DECODE_EN :1,
+ :7,
+ PCHHOT_SML1ALERT_SEL :1,
+ :9;
+ };
+ struct { /* STRP10 */
+ uint32_t :1,
+ ME_BOOT_FLASH :1,
+ :6,
+ MDSMBE_EN :1,
+ MDSMBE_ADD :7,
+ :2,
+ ICC_SEL :3,
+ MER_CL1 :1,
+ ICC_PRO_SEL :1,
+ Deep_SX_EN :1,
+ ME_DBG_LAN :1,
+ :7;
+ };
+ struct { /* STRP11 */
+ uint32_t SML1GPAEN :1,
+ SML1GPA :7,
+ :16,
+ SML1I2CAEN :1,
+ SML1I2CA :7;
+ };
+ struct { /* STRP12 */
+ uint32_t :32;
+ };
+ struct { /* STRP13 */
+ uint32_t :32;
+ };
+ struct { /* STRP14 */
+ uint32_t :32;
+ };
+ struct { /* STRP15 */
+ uint32_t cs_ss6 :6,
+ IWL_EN :1,
+ cs_ss5 :2,
+ :4,
+ SMLINK1_THERM_SEL :1,
+ SLP_LAN_GP29_SEL :1,
+ :16;
+ };
+ struct { /* STRP16 */
+ uint32_t :32;
+ };
+ struct { /* STRP17 */
+ uint32_t ICML :1,
+ cs_ss7 :1,
+ :30;
+ };
+ } cougar;
+ };
+};
+
+struct ich_desc_upper_map {
+ union {
+ uint32_t FLUMAP1; /* Flash Upper Map 1 */
+ struct {
+ uint32_t VTBA :8, /* ME VSCC Table Base Address */
+ VTL :8, /* ME VSCC Table Length */
+ :16;
+ };
+ };
+ struct {
+ union { /* JEDEC-ID Register */
+ uint32_t JID;
+ struct {
+ uint32_t vid :8, /* Vendor ID */
+ cid0 :8, /* Component ID 0 */
+ cid1 :8, /* Component ID 1 */
+ :8;
+ };
+ };
+ union { /* Vendor Specific Component Capabilities */
+ uint32_t VSCC;
+ struct {
+ uint32_t ubes :2, /* Upper Block/Sector Erase Size */
+ uwg :1, /* Upper Write Granularity */
+ uwsr :1, /* Upper Write Status Required */
+ uwews :1, /* Upper Write Enable on Write Status */
+ :3,
+ ueo :8, /* Upper Erase Opcode */
+ lbes :2, /* Lower Block/Sector Erase Size */
+ lwg :1, /* Lower Write Granularity */
+ lwsr :1, /* Lower Write Status Required */
+ lwews :1, /* Lower Write Enable on Write Status */
+ :3,
+ leo :16; /* Lower Erase Opcode */
+ };
+ };
+ } vscc_table[128];
+};
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+
+struct ich_descriptors {
+ struct ich_desc_content content;
+ struct ich_desc_component component;
+ struct ich_desc_region region;
+ struct ich_desc_master master;
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+ struct ich_desc_north_strap north;
+ struct ich_desc_south_strap south;
+ struct ich_desc_upper_map upper;
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+};
+
+void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc);
+
+void prettyprint_ich_descriptor_content(const struct ich_desc_content *content);
+void prettyprint_ich_descriptor_component(enum ich_chipset cs, const struct ich_descriptors *desc);
+void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc);
+void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
+
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+void prettyprint_ich_descriptor_upper_map(const struct ich_desc_upper_map *umap);
+void prettyprint_ich_descriptor_straps(enum ich_chipset cs, const struct ich_descriptors *desc);
+int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struct ich_descriptors *desc);
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
+int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
+int getFCBA_component_density(enum ich_chipset cs, const struct ich_descriptors *desc, uint8_t idx);
+
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+#endif /* __ICH_DESCRIPTORS_H__ */
+#endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/ichspi.c b/ichspi.c
new file mode 100644
index 0000000..e1395ee
--- /dev/null
+++ b/ichspi.c
@@ -0,0 +1,1881 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 Stefan Wildemann <stefan.wildemann@kontron.com>
+ * Copyright (C) 2008 Claus Gindhart <claus.gindhart@kontron.com>
+ * Copyright (C) 2008 Dominik Geyer <dominik.geyer@kontron.com>
+ * Copyright (C) 2008 coresystems GmbH <info@coresystems.de>
+ * Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2011 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+#include "ich_descriptors.h"
+
+/* ICH9 controller register definition */
+#define ICH9_REG_HSFS 0x04 /* 16 Bits Hardware Sequencing Flash Status */
+#define HSFS_FDONE_OFF 0 /* 0: Flash Cycle Done */
+#define HSFS_FDONE (0x1 << HSFS_FDONE_OFF)
+#define HSFS_FCERR_OFF 1 /* 1: Flash Cycle Error */
+#define HSFS_FCERR (0x1 << HSFS_FCERR_OFF)
+#define HSFS_AEL_OFF 2 /* 2: Access Error Log */
+#define HSFS_AEL (0x1 << HSFS_AEL_OFF)
+#define HSFS_BERASE_OFF 3 /* 3-4: Block/Sector Erase Size */
+#define HSFS_BERASE (0x3 << HSFS_BERASE_OFF)
+#define HSFS_SCIP_OFF 5 /* 5: SPI Cycle In Progress */
+#define HSFS_SCIP (0x1 << HSFS_SCIP_OFF)
+ /* 6-12: reserved */
+#define HSFS_FDOPSS_OFF 13 /* 13: Flash Descriptor Override Pin-Strap Status */
+#define HSFS_FDOPSS (0x1 << HSFS_FDOPSS_OFF)
+#define HSFS_FDV_OFF 14 /* 14: Flash Descriptor Valid */
+#define HSFS_FDV (0x1 << HSFS_FDV_OFF)
+#define HSFS_FLOCKDN_OFF 15 /* 15: Flash Configuration Lock-Down */
+#define HSFS_FLOCKDN (0x1 << HSFS_FLOCKDN_OFF)
+
+#define ICH9_REG_HSFC 0x06 /* 16 Bits Hardware Sequencing Flash Control */
+#define HSFC_FGO_OFF 0 /* 0: Flash Cycle Go */
+#define HSFC_FGO (0x1 << HSFC_FGO_OFF)
+#define HSFC_FCYCLE_OFF 1 /* 1-2: FLASH Cycle */
+#define HSFC_FCYCLE (0x3 << HSFC_FCYCLE_OFF)
+ /* 3-7: reserved */
+#define HSFC_FDBC_OFF 8 /* 8-13: Flash Data Byte Count */
+#define HSFC_FDBC (0x3f << HSFC_FDBC_OFF)
+ /* 14: reserved */
+#define HSFC_SME_OFF 15 /* 15: SPI SMI# Enable */
+#define HSFC_SME (0x1 << HSFC_SME_OFF)
+
+#define ICH9_REG_FADDR 0x08 /* 32 Bits */
+#define ICH9_REG_FDATA0 0x10 /* 64 Bytes */
+
+#define ICH9_REG_FRAP 0x50 /* 32 Bytes Flash Region Access Permissions */
+#define ICH9_REG_FREG0 0x54 /* 32 Bytes Flash Region 0 */
+
+#define ICH9_REG_PR0 0x74 /* 32 Bytes Protected Range 0 */
+#define PR_WP_OFF 31 /* 31: write protection enable */
+#define PR_RP_OFF 15 /* 15: read protection enable */
+
+#define ICH9_REG_SSFS 0x90 /* 08 Bits */
+#define SSFS_SCIP_OFF 0 /* SPI Cycle In Progress */
+#define SSFS_SCIP (0x1 << SSFS_SCIP_OFF)
+#define SSFS_FDONE_OFF 2 /* Cycle Done Status */
+#define SSFS_FDONE (0x1 << SSFS_FDONE_OFF)
+#define SSFS_FCERR_OFF 3 /* Flash Cycle Error */
+#define SSFS_FCERR (0x1 << SSFS_FCERR_OFF)
+#define SSFS_AEL_OFF 4 /* Access Error Log */
+#define SSFS_AEL (0x1 << SSFS_AEL_OFF)
+/* The following bits are reserved in SSFS: 1,5-7. */
+#define SSFS_RESERVED_MASK 0x000000e2
+
+#define ICH9_REG_SSFC 0x91 /* 24 Bits */
+/* We combine SSFS and SSFC to one 32-bit word,
+ * therefore SSFC bits are off by 8. */
+ /* 0: reserved */
+#define SSFC_SCGO_OFF (1 + 8) /* 1: SPI Cycle Go */
+#define SSFC_SCGO (0x1 << SSFC_SCGO_OFF)
+#define SSFC_ACS_OFF (2 + 8) /* 2: Atomic Cycle Sequence */
+#define SSFC_ACS (0x1 << SSFC_ACS_OFF)
+#define SSFC_SPOP_OFF (3 + 8) /* 3: Sequence Prefix Opcode Pointer */
+#define SSFC_SPOP (0x1 << SSFC_SPOP_OFF)
+#define SSFC_COP_OFF (4 + 8) /* 4-6: Cycle Opcode Pointer */
+#define SSFC_COP (0x7 << SSFC_COP_OFF)
+ /* 7: reserved */
+#define SSFC_DBC_OFF (8 + 8) /* 8-13: Data Byte Count */
+#define SSFC_DBC (0x3f << SSFC_DBC_OFF)
+#define SSFC_DS_OFF (14 + 8) /* 14: Data Cycle */
+#define SSFC_DS (0x1 << SSFC_DS_OFF)
+#define SSFC_SME_OFF (15 + 8) /* 15: SPI SMI# Enable */
+#define SSFC_SME (0x1 << SSFC_SME_OFF)
+#define SSFC_SCF_OFF (16 + 8) /* 16-18: SPI Cycle Frequency */
+#define SSFC_SCF (0x7 << SSFC_SCF_OFF)
+#define SSFC_SCF_20MHZ 0x00000000
+#define SSFC_SCF_33MHZ 0x01000000
+ /* 19-23: reserved */
+#define SSFC_RESERVED_MASK 0xf8008100
+
+#define ICH9_REG_PREOP 0x94 /* 16 Bits */
+#define ICH9_REG_OPTYPE 0x96 /* 16 Bits */
+#define ICH9_REG_OPMENU 0x98 /* 64 Bits */
+
+#define ICH9_REG_BBAR 0xA0 /* 32 Bits BIOS Base Address Configuration */
+#define BBAR_MASK 0x00ffff00 /* 8-23: Bottom of System Flash */
+
+#define ICH8_REG_VSCC 0xC1 /* 32 Bits Vendor Specific Component Capabilities */
+#define ICH9_REG_LVSCC 0xC4 /* 32 Bits Host Lower Vendor Specific Component Capabilities */
+#define ICH9_REG_UVSCC 0xC8 /* 32 Bits Host Upper Vendor Specific Component Capabilities */
+/* The individual fields of the VSCC registers are defined in the file
+ * ich_descriptors.h. The reason is that the same layout is also used in the
+ * flash descriptor to define the properties of the different flash chips
+ * supported. The BIOS (or the ME?) is responsible to populate the ICH registers
+ * with the information from the descriptor on startup depending on the actual
+ * chip(s) detected. */
+
+#define ICH9_REG_FPB 0xD0 /* 32 Bits Flash Partition Boundary */
+#define FPB_FPBA_OFF 0 /* 0-12: Block/Sector Erase Size */
+#define FPB_FPBA (0x1FFF << FPB_FPBA_OFF)
+
+// ICH9R SPI commands
+#define SPI_OPCODE_TYPE_READ_NO_ADDRESS 0
+#define SPI_OPCODE_TYPE_WRITE_NO_ADDRESS 1
+#define SPI_OPCODE_TYPE_READ_WITH_ADDRESS 2
+#define SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS 3
+
+// ICH7 registers
+#define ICH7_REG_SPIS 0x00 /* 16 Bits */
+#define SPIS_SCIP 0x0001
+#define SPIS_GRANT 0x0002
+#define SPIS_CDS 0x0004
+#define SPIS_FCERR 0x0008
+#define SPIS_RESERVED_MASK 0x7ff0
+
+/* VIA SPI is compatible with ICH7, but maxdata
+ to transfer is 16 bytes.
+
+ DATA byte count on ICH7 is 8:13, on VIA 8:11
+
+ bit 12 is port select CS0 CS1
+ bit 13 is FAST READ enable
+ bit 7 is used with fast read and one shot controls CS de-assert?
+*/
+
+#define ICH7_REG_SPIC 0x02 /* 16 Bits */
+#define SPIC_SCGO 0x0002
+#define SPIC_ACS 0x0004
+#define SPIC_SPOP 0x0008
+#define SPIC_DS 0x4000
+
+#define ICH7_REG_SPIA 0x04 /* 32 Bits */
+#define ICH7_REG_SPID0 0x08 /* 64 Bytes */
+#define ICH7_REG_PREOP 0x54 /* 16 Bits */
+#define ICH7_REG_OPTYPE 0x56 /* 16 Bits */
+#define ICH7_REG_OPMENU 0x58 /* 64 Bits */
+
+/* ICH SPI configuration lock-down. May be set during chipset enabling. */
+static int ichspi_lock = 0;
+
+static enum ich_chipset ich_generation = CHIPSET_ICH_UNKNOWN;
+uint32_t ichspi_bbar = 0;
+
+static void *ich_spibar = NULL;
+
+typedef struct _OPCODE {
+ uint8_t opcode; //This commands spi opcode
+ uint8_t spi_type; //This commands spi type
+ uint8_t atomic; //Use preop: (0: none, 1: preop0, 2: preop1
+} OPCODE;
+
+/* Suggested opcode definition:
+ * Preop 1: Write Enable
+ * Preop 2: Write Status register enable
+ *
+ * OP 0: Write address
+ * OP 1: Read Address
+ * OP 2: ERASE block
+ * OP 3: Read Status register
+ * OP 4: Read ID
+ * OP 5: Write Status register
+ * OP 6: chip private (read JEDEC id)
+ * OP 7: Chip erase
+ */
+typedef struct _OPCODES {
+ uint8_t preop[2];
+ OPCODE opcode[8];
+} OPCODES;
+
+static OPCODES *curopcodes = NULL;
+
+/* HW access functions */
+static uint32_t REGREAD32(int X)
+{
+ return mmio_readl(ich_spibar + X);
+}
+
+static uint16_t REGREAD16(int X)
+{
+ return mmio_readw(ich_spibar + X);
+}
+
+static uint16_t REGREAD8(int X)
+{
+ return mmio_readb(ich_spibar + X);
+}
+
+#define REGWRITE32(off, val) mmio_writel(val, ich_spibar+(off))
+#define REGWRITE16(off, val) mmio_writew(val, ich_spibar+(off))
+#define REGWRITE8(off, val) mmio_writeb(val, ich_spibar+(off))
+
+/* Common SPI functions */
+static int find_opcode(OPCODES *op, uint8_t opcode);
+static int find_preop(OPCODES *op, uint8_t preop);
+static int generate_opcodes(OPCODES * op);
+static int program_opcodes(OPCODES *op, int enable_undo);
+static int run_opcode(const struct flashctx *flash, OPCODE op, uint32_t offset,
+ uint8_t datalength, uint8_t * data);
+
+/* for pairing opcodes with their required preop */
+struct preop_opcode_pair {
+ uint8_t preop;
+ uint8_t opcode;
+};
+
+/* List of opcodes which need preopcodes and matching preopcodes. Unused. */
+const struct preop_opcode_pair pops[] = {
+ {JEDEC_WREN, JEDEC_BYTE_PROGRAM},
+ {JEDEC_WREN, JEDEC_SE}, /* sector erase */
+ {JEDEC_WREN, JEDEC_BE_52}, /* block erase */
+ {JEDEC_WREN, JEDEC_BE_D8}, /* block erase */
+ {JEDEC_WREN, JEDEC_CE_60}, /* chip erase */
+ {JEDEC_WREN, JEDEC_CE_C7}, /* chip erase */
+ /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
+ {JEDEC_WREN, JEDEC_WRSR},
+ {JEDEC_EWSR, JEDEC_WRSR},
+ {0,}
+};
+
+/* Reasonable default configuration. Needs ad-hoc modifications if we
+ * encounter unlisted opcodes. Fun.
+ */
+static OPCODES O_ST_M25P = {
+ {
+ JEDEC_WREN,
+ JEDEC_EWSR,
+ },
+ {
+ {JEDEC_BYTE_PROGRAM, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Write Byte
+ {JEDEC_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Data
+ {JEDEC_BE_D8, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Erase Sector
+ {JEDEC_RDSR, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read Device Status Reg
+ {JEDEC_REMS, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Electronic Manufacturer Signature
+ {JEDEC_WRSR, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Write Status Register
+ {JEDEC_RDID, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read JDEC ID
+ {JEDEC_CE_C7, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Bulk erase
+ }
+};
+
+/* List of opcodes with their corresponding spi_type
+ * It is used to reprogram the chipset OPCODE table on-the-fly if an opcode
+ * is needed which is currently not in the chipset OPCODE table
+ */
+static OPCODE POSSIBLE_OPCODES[] = {
+ {JEDEC_BYTE_PROGRAM, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Write Byte
+ {JEDEC_READ, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Data
+ {JEDEC_BE_D8, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Erase Sector
+ {JEDEC_RDSR, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read Device Status Reg
+ {JEDEC_REMS, SPI_OPCODE_TYPE_READ_WITH_ADDRESS, 0}, // Read Electronic Manufacturer Signature
+ {JEDEC_WRSR, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Write Status Register
+ {JEDEC_RDID, SPI_OPCODE_TYPE_READ_NO_ADDRESS, 0}, // Read JDEC ID
+ {JEDEC_CE_C7, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Bulk erase
+ {JEDEC_SE, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Sector erase
+ {JEDEC_BE_52, SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS, 0}, // Block erase
+ {JEDEC_AAI_WORD_PROGRAM, SPI_OPCODE_TYPE_WRITE_NO_ADDRESS, 0}, // Auto Address Increment
+};
+
+static OPCODES O_EXISTING = {};
+
+/* pretty printing functions */
+static void prettyprint_opcodes(OPCODES *ops)
+{
+ OPCODE oc;
+ const char *t;
+ const char *a;
+ uint8_t i;
+ static const char *const spi_type[4] = {
+ "read w/o addr",
+ "write w/o addr",
+ "read w/ addr",
+ "write w/ addr"
+ };
+ static const char *const atomic_type[3] = {
+ "none",
+ " 0 ",
+ " 1 "
+ };
+
+ if (ops == NULL)
+ return;
+
+ msg_pdbg2(" OP Type Pre-OP\n");
+ for (i = 0; i < 8; i++) {
+ oc = ops->opcode[i];
+ t = (oc.spi_type > 3) ? "invalid" : spi_type[oc.spi_type];
+ a = (oc.atomic > 2) ? "invalid" : atomic_type[oc.atomic];
+ msg_pdbg2("op[%d]: 0x%02x, %s, %s\n", i, oc.opcode, t, a);
+ }
+ msg_pdbg2("Pre-OP 0: 0x%02x, Pre-OP 1: 0x%02x\n", ops->preop[0],
+ ops->preop[1]);
+}
+
+#define pprint_reg(reg, bit, val, sep) msg_pdbg("%s=%d" sep, #bit, (val & reg##_##bit)>>reg##_##bit##_OFF)
+
+static void prettyprint_ich9_reg_hsfs(uint16_t reg_val)
+{
+ msg_pdbg("HSFS: ");
+ pprint_reg(HSFS, FDONE, reg_val, ", ");
+ pprint_reg(HSFS, FCERR, reg_val, ", ");
+ pprint_reg(HSFS, AEL, reg_val, ", ");
+ pprint_reg(HSFS, BERASE, reg_val, ", ");
+ pprint_reg(HSFS, SCIP, reg_val, ", ");
+ pprint_reg(HSFS, FDOPSS, reg_val, ", ");
+ pprint_reg(HSFS, FDV, reg_val, ", ");
+ pprint_reg(HSFS, FLOCKDN, reg_val, "\n");
+}
+
+static void prettyprint_ich9_reg_hsfc(uint16_t reg_val)
+{
+ msg_pdbg("HSFC: ");
+ pprint_reg(HSFC, FGO, reg_val, ", ");
+ pprint_reg(HSFC, FCYCLE, reg_val, ", ");
+ pprint_reg(HSFC, FDBC, reg_val, ", ");
+ pprint_reg(HSFC, SME, reg_val, "\n");
+}
+
+static void prettyprint_ich9_reg_ssfs(uint32_t reg_val)
+{
+ msg_pdbg("SSFS: ");
+ pprint_reg(SSFS, SCIP, reg_val, ", ");
+ pprint_reg(SSFS, FDONE, reg_val, ", ");
+ pprint_reg(SSFS, FCERR, reg_val, ", ");
+ pprint_reg(SSFS, AEL, reg_val, "\n");
+}
+
+static void prettyprint_ich9_reg_ssfc(uint32_t reg_val)
+{
+ msg_pdbg("SSFC: ");
+ pprint_reg(SSFC, SCGO, reg_val, ", ");
+ pprint_reg(SSFC, ACS, reg_val, ", ");
+ pprint_reg(SSFC, SPOP, reg_val, ", ");
+ pprint_reg(SSFC, COP, reg_val, ", ");
+ pprint_reg(SSFC, DBC, reg_val, ", ");
+ pprint_reg(SSFC, SME, reg_val, ", ");
+ pprint_reg(SSFC, SCF, reg_val, "\n");
+}
+
+static uint8_t lookup_spi_type(uint8_t opcode)
+{
+ int a;
+
+ for (a = 0; a < ARRAY_SIZE(POSSIBLE_OPCODES); a++) {
+ if (POSSIBLE_OPCODES[a].opcode == opcode)
+ return POSSIBLE_OPCODES[a].spi_type;
+ }
+
+ return 0xFF;
+}
+
+static int reprogram_opcode_on_the_fly(uint8_t opcode, unsigned int writecnt, unsigned int readcnt)
+{
+ uint8_t spi_type;
+
+ spi_type = lookup_spi_type(opcode);
+ if (spi_type > 3) {
+ /* Try to guess spi type from read/write sizes.
+ * The following valid writecnt/readcnt combinations exist:
+ * writecnt = 4, readcnt >= 0
+ * writecnt = 1, readcnt >= 0
+ * writecnt >= 4, readcnt = 0
+ * writecnt >= 1, readcnt = 0
+ * writecnt >= 1 is guaranteed for all commands.
+ */
+ if (readcnt == 0)
+ /* if readcnt=0 and writecount >= 4, we don't know if it is WRITE_NO_ADDRESS
+ * or WRITE_WITH_ADDRESS. But if we use WRITE_NO_ADDRESS and the first 3 data
+ * bytes are actual the address, they go to the bus anyhow
+ */
+ spi_type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
+ else if (writecnt == 1) // and readcnt is > 0
+ spi_type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
+ else if (writecnt == 4) // and readcnt is > 0
+ spi_type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
+ else // we have an invalid case
+ return SPI_INVALID_LENGTH;
+ }
+ int oppos = 2; // use original JEDEC_BE_D8 offset
+ curopcodes->opcode[oppos].opcode = opcode;
+ curopcodes->opcode[oppos].spi_type = spi_type;
+ program_opcodes(curopcodes, 0);
+ oppos = find_opcode(curopcodes, opcode);
+ msg_pdbg2("on-the-fly OPCODE (0x%02X) re-programmed, op-pos=%d\n", opcode, oppos);
+ return oppos;
+}
+
+static int find_opcode(OPCODES *op, uint8_t opcode)
+{
+ int a;
+
+ if (op == NULL) {
+ msg_perr("\n%s: null OPCODES pointer!\n", __func__);
+ return -1;
+ }
+
+ for (a = 0; a < 8; a++) {
+ if (op->opcode[a].opcode == opcode)
+ return a;
+ }
+
+ return -1;
+}
+
+static int find_preop(OPCODES *op, uint8_t preop)
+{
+ int a;
+
+ if (op == NULL) {
+ msg_perr("\n%s: null OPCODES pointer!\n", __func__);
+ return -1;
+ }
+
+ for (a = 0; a < 2; a++) {
+ if (op->preop[a] == preop)
+ return a;
+ }
+
+ return -1;
+}
+
+/* Create a struct OPCODES based on what we find in the locked down chipset. */
+static int generate_opcodes(OPCODES * op)
+{
+ int a;
+ uint16_t preop, optype;
+ uint32_t opmenu[2];
+
+ if (op == NULL) {
+ msg_perr("\n%s: null OPCODES pointer!\n", __func__);
+ return -1;
+ }
+
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ preop = REGREAD16(ICH7_REG_PREOP);
+ optype = REGREAD16(ICH7_REG_OPTYPE);
+ opmenu[0] = REGREAD32(ICH7_REG_OPMENU);
+ opmenu[1] = REGREAD32(ICH7_REG_OPMENU + 4);
+ break;
+ case CHIPSET_ICH8:
+ default: /* Future version might behave the same */
+ preop = REGREAD16(ICH9_REG_PREOP);
+ optype = REGREAD16(ICH9_REG_OPTYPE);
+ opmenu[0] = REGREAD32(ICH9_REG_OPMENU);
+ opmenu[1] = REGREAD32(ICH9_REG_OPMENU + 4);
+ break;
+ }
+
+ op->preop[0] = (uint8_t) preop;
+ op->preop[1] = (uint8_t) (preop >> 8);
+
+ for (a = 0; a < 8; a++) {
+ op->opcode[a].spi_type = (uint8_t) (optype & 0x3);
+ optype >>= 2;
+ }
+
+ for (a = 0; a < 4; a++) {
+ op->opcode[a].opcode = (uint8_t) (opmenu[0] & 0xff);
+ opmenu[0] >>= 8;
+ }
+
+ for (a = 4; a < 8; a++) {
+ op->opcode[a].opcode = (uint8_t) (opmenu[1] & 0xff);
+ opmenu[1] >>= 8;
+ }
+
+ /* No preopcodes used by default. */
+ for (a = 0; a < 8; a++)
+ op->opcode[a].atomic = 0;
+
+ return 0;
+}
+
+static int program_opcodes(OPCODES *op, int enable_undo)
+{
+ uint8_t a;
+ uint16_t preop, optype;
+ uint32_t opmenu[2];
+
+ /* Program Prefix Opcodes */
+ /* 0:7 Prefix Opcode 1 */
+ preop = (op->preop[0]);
+ /* 8:16 Prefix Opcode 2 */
+ preop |= ((uint16_t) op->preop[1]) << 8;
+
+ /* Program Opcode Types 0 - 7 */
+ optype = 0;
+ for (a = 0; a < 8; a++) {
+ optype |= ((uint16_t) op->opcode[a].spi_type) << (a * 2);
+ }
+
+ /* Program Allowable Opcodes 0 - 3 */
+ opmenu[0] = 0;
+ for (a = 0; a < 4; a++) {
+ opmenu[0] |= ((uint32_t) op->opcode[a].opcode) << (a * 8);
+ }
+
+ /* Program Allowable Opcodes 4 - 7 */
+ opmenu[1] = 0;
+ for (a = 4; a < 8; a++) {
+ opmenu[1] |= ((uint32_t) op->opcode[a].opcode) << ((a - 4) * 8);
+ }
+
+ msg_pdbg2("\n%s: preop=%04x optype=%04x opmenu=%08x%08x\n", __func__, preop, optype, opmenu[0], opmenu[1]);
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ /* Register undo only for enable_undo=1, i.e. first call. */
+ if (enable_undo) {
+ rmmio_valw(ich_spibar + ICH7_REG_PREOP);
+ rmmio_valw(ich_spibar + ICH7_REG_OPTYPE);
+ rmmio_vall(ich_spibar + ICH7_REG_OPMENU);
+ rmmio_vall(ich_spibar + ICH7_REG_OPMENU + 4);
+ }
+ mmio_writew(preop, ich_spibar + ICH7_REG_PREOP);
+ mmio_writew(optype, ich_spibar + ICH7_REG_OPTYPE);
+ mmio_writel(opmenu[0], ich_spibar + ICH7_REG_OPMENU);
+ mmio_writel(opmenu[1], ich_spibar + ICH7_REG_OPMENU + 4);
+ break;
+ case CHIPSET_ICH8:
+ default: /* Future version might behave the same */
+ /* Register undo only for enable_undo=1, i.e. first call. */
+ if (enable_undo) {
+ rmmio_valw(ich_spibar + ICH9_REG_PREOP);
+ rmmio_valw(ich_spibar + ICH9_REG_OPTYPE);
+ rmmio_vall(ich_spibar + ICH9_REG_OPMENU);
+ rmmio_vall(ich_spibar + ICH9_REG_OPMENU + 4);
+ }
+ mmio_writew(preop, ich_spibar + ICH9_REG_PREOP);
+ mmio_writew(optype, ich_spibar + ICH9_REG_OPTYPE);
+ mmio_writel(opmenu[0], ich_spibar + ICH9_REG_OPMENU);
+ mmio_writel(opmenu[1], ich_spibar + ICH9_REG_OPMENU + 4);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns -1 if at least one mandatory opcode is inaccessible, 0 otherwise.
+ * FIXME: this should also check for
+ * - at least one probing opcode (RDID (incl. AT25F variants?), REMS, RES?)
+ * - at least one erasing opcode (lots.)
+ * - at least one program opcode (BYTE_PROGRAM, AAI_WORD_PROGRAM, ...?)
+ * - necessary preops? (EWSR, WREN, ...?)
+ */
+static int ich_missing_opcodes()
+{
+ uint8_t ops[] = {
+ JEDEC_READ,
+ JEDEC_RDSR,
+ 0
+ };
+ int i = 0;
+ while (ops[i] != 0) {
+ msg_pspew("checking for opcode 0x%02x\n", ops[i]);
+ if (find_opcode(curopcodes, ops[i]) == -1)
+ return -1;
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * Try to set BBAR (BIOS Base Address Register), but read back the value in case
+ * it didn't stick.
+ */
+static void ich_set_bbar(uint32_t min_addr)
+{
+ int bbar_off;
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ bbar_off = 0x50;
+ break;
+ case CHIPSET_ICH8:
+ case CHIPSET_BAYTRAIL:
+ msg_pdbg("BBAR offset is unknown!\n");
+ return;
+ case CHIPSET_ICH9:
+ default: /* Future version might behave the same */
+ bbar_off = ICH9_REG_BBAR;
+ break;
+ }
+
+ ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & ~BBAR_MASK;
+ if (ichspi_bbar) {
+ msg_pdbg("Reserved bits in BBAR not zero: 0x%08x\n",
+ ichspi_bbar);
+ }
+ min_addr &= BBAR_MASK;
+ ichspi_bbar |= min_addr;
+ rmmio_writel(ichspi_bbar, ich_spibar + bbar_off);
+ ichspi_bbar = mmio_readl(ich_spibar + bbar_off) & BBAR_MASK;
+
+ /* We don't have any option except complaining. And if the write
+ * failed, the restore will fail as well, so no problem there.
+ */
+ if (ichspi_bbar != min_addr)
+ msg_perr("Setting BBAR to 0x%08x failed! New value: 0x%08x.\n",
+ min_addr, ichspi_bbar);
+}
+
+/* Read len bytes from the fdata/spid register into the data array.
+ *
+ * Note that using len > flash->mst->spi.max_data_read will return garbage or
+ * may even crash.
+ */
+static void ich_read_data(uint8_t *data, int len, int reg0_off)
+ {
+ int i;
+ uint32_t temp32 = 0;
+
+ for (i = 0; i < len; i++) {
+ if ((i % 4) == 0)
+ temp32 = REGREAD32(reg0_off + i);
+
+ data[i] = (temp32 >> ((i % 4) * 8)) & 0xff;
+ }
+}
+
+/* Fill len bytes from the data array into the fdata/spid registers.
+ *
+ * Note that using len > flash->mst->spi.max_data_write will trash the registers
+ * following the data registers.
+ */
+static void ich_fill_data(const uint8_t *data, int len, int reg0_off)
+{
+ uint32_t temp32 = 0;
+ int i;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++) {
+ if ((i % 4) == 0)
+ temp32 = 0;
+
+ temp32 |= ((uint32_t) data[i]) << ((i % 4) * 8);
+
+ if ((i % 4) == 3) /* 32 bits are full, write them to regs. */
+ REGWRITE32(reg0_off + (i - (i % 4)), temp32);
+ }
+ i--;
+ if ((i % 4) != 3) /* Write remaining data to regs. */
+ REGWRITE32(reg0_off + (i - (i % 4)), temp32);
+}
+
+/* This function generates OPCODES from or programs OPCODES to ICH according to
+ * the chipset's SPI configuration lock.
+ *
+ * It should be called before ICH sends any spi command.
+ */
+static int ich_init_opcodes(void)
+{
+ int rc = 0;
+ OPCODES *curopcodes_done;
+
+ if (curopcodes)
+ return 0;
+
+ if (ichspi_lock) {
+ msg_pdbg("Reading OPCODES... ");
+ curopcodes_done = &O_EXISTING;
+ rc = generate_opcodes(curopcodes_done);
+ } else {
+ msg_pdbg("Programming OPCODES... ");
+ curopcodes_done = &O_ST_M25P;
+ rc = program_opcodes(curopcodes_done, 1);
+ }
+
+ if (rc) {
+ curopcodes = NULL;
+ msg_perr("failed\n");
+ return 1;
+ } else {
+ curopcodes = curopcodes_done;
+ msg_pdbg("done\n");
+ prettyprint_opcodes(curopcodes);
+ return 0;
+ }
+}
+
+static int ich7_run_opcode(OPCODE op, uint32_t offset,
+ uint8_t datalength, uint8_t * data, int maxdata)
+{
+ int write_cmd = 0;
+ int timeout;
+ uint32_t temp32;
+ uint16_t temp16;
+ uint64_t opmenu;
+ int opcode_index;
+
+ /* Is it a write command? */
+ if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)
+ || (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) {
+ write_cmd = 1;
+ }
+
+ timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
+ while ((REGREAD16(ICH7_REG_SPIS) & SPIS_SCIP) && --timeout) {
+ programmer_delay(10);
+ }
+ if (!timeout) {
+ msg_perr("Error: SCIP never cleared!\n");
+ return 1;
+ }
+
+ /* Program offset in flash into SPIA while preserving reserved bits. */
+ temp32 = REGREAD32(ICH7_REG_SPIA) & ~0x00FFFFFF;
+ REGWRITE32(ICH7_REG_SPIA, (offset & 0x00FFFFFF) | temp32);
+
+ /* Program data into SPID0 to N */
+ if (write_cmd && (datalength != 0))
+ ich_fill_data(data, datalength, ICH7_REG_SPID0);
+
+ /* Assemble SPIS */
+ temp16 = REGREAD16(ICH7_REG_SPIS);
+ /* keep reserved bits */
+ temp16 &= SPIS_RESERVED_MASK;
+ /* clear error status registers */
+ temp16 |= (SPIS_CDS | SPIS_FCERR);
+ REGWRITE16(ICH7_REG_SPIS, temp16);
+
+ /* Assemble SPIC */
+ temp16 = 0;
+
+ if (datalength != 0) {
+ temp16 |= SPIC_DS;
+ temp16 |= ((uint32_t) ((datalength - 1) & (maxdata - 1))) << 8;
+ }
+
+ /* Select opcode */
+ opmenu = REGREAD32(ICH7_REG_OPMENU);
+ opmenu |= ((uint64_t)REGREAD32(ICH7_REG_OPMENU + 4)) << 32;
+
+ for (opcode_index = 0; opcode_index < 8; opcode_index++) {
+ if ((opmenu & 0xff) == op.opcode) {
+ break;
+ }
+ opmenu >>= 8;
+ }
+ if (opcode_index == 8) {
+ msg_pdbg("Opcode %x not found.\n", op.opcode);
+ return 1;
+ }
+ temp16 |= ((uint16_t) (opcode_index & 0x07)) << 4;
+
+ timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
+ /* Handle Atomic. Atomic commands include three steps:
+ - sending the preop (mainly EWSR or WREN)
+ - sending the main command
+ - waiting for the busy bit (WIP) to be cleared
+ This means the timeout must be sufficient for chip erase
+ of slow high-capacity chips.
+ */
+ switch (op.atomic) {
+ case 2:
+ /* Select second preop. */
+ temp16 |= SPIC_SPOP;
+ /* And fall through. */
+ case 1:
+ /* Atomic command (preop+op) */
+ temp16 |= SPIC_ACS;
+ timeout = 100 * 1000 * 60; /* 60 seconds */
+ break;
+ }
+
+ /* Start */
+ temp16 |= SPIC_SCGO;
+
+ /* write it */
+ REGWRITE16(ICH7_REG_SPIC, temp16);
+
+ /* Wait for Cycle Done Status or Flash Cycle Error. */
+ while (((REGREAD16(ICH7_REG_SPIS) & (SPIS_CDS | SPIS_FCERR)) == 0) &&
+ --timeout) {
+ programmer_delay(10);
+ }
+ if (!timeout) {
+ msg_perr("timeout, ICH7_REG_SPIS=0x%04x\n",
+ REGREAD16(ICH7_REG_SPIS));
+ return 1;
+ }
+
+ /* FIXME: make sure we do not needlessly cause transaction errors. */
+ temp16 = REGREAD16(ICH7_REG_SPIS);
+ if (temp16 & SPIS_FCERR) {
+ msg_perr("Transaction error!\n");
+ /* keep reserved bits */
+ temp16 &= SPIS_RESERVED_MASK;
+ REGWRITE16(ICH7_REG_SPIS, temp16 | SPIS_FCERR);
+ return 1;
+ }
+
+ if ((!write_cmd) && (datalength != 0))
+ ich_read_data(data, datalength, ICH7_REG_SPID0);
+
+ return 0;
+}
+
+static int ich9_run_opcode(OPCODE op, uint32_t offset,
+ uint8_t datalength, uint8_t * data)
+{
+ int write_cmd = 0;
+ int timeout;
+ uint32_t temp32;
+ uint64_t opmenu;
+ int opcode_index;
+
+ /* Is it a write command? */
+ if ((op.spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)
+ || (op.spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS)) {
+ write_cmd = 1;
+ }
+
+ timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
+ while ((REGREAD8(ICH9_REG_SSFS) & SSFS_SCIP) && --timeout) {
+ programmer_delay(10);
+ }
+ if (!timeout) {
+ msg_perr("Error: SCIP never cleared!\n");
+ return 1;
+ }
+
+ /* Program offset in flash into FADDR while preserve the reserved bits
+ * and clearing the 25. address bit which is only useable in hwseq. */
+ temp32 = REGREAD32(ICH9_REG_FADDR) & ~0x01FFFFFF;
+ REGWRITE32(ICH9_REG_FADDR, (offset & 0x00FFFFFF) | temp32);
+
+ /* Program data into FDATA0 to N */
+ if (write_cmd && (datalength != 0))
+ ich_fill_data(data, datalength, ICH9_REG_FDATA0);
+
+ /* Assemble SSFS + SSFC */
+ temp32 = REGREAD32(ICH9_REG_SSFS);
+ /* Keep reserved bits only */
+ temp32 &= SSFS_RESERVED_MASK | SSFC_RESERVED_MASK;
+ /* Clear cycle done and cycle error status registers */
+ temp32 |= (SSFS_FDONE | SSFS_FCERR);
+ REGWRITE32(ICH9_REG_SSFS, temp32);
+
+ /* Use 20 MHz */
+ temp32 |= SSFC_SCF_20MHZ;
+
+ /* Set data byte count (DBC) and data cycle bit (DS) */
+ if (datalength != 0) {
+ uint32_t datatemp;
+ temp32 |= SSFC_DS;
+ datatemp = ((((uint32_t)datalength - 1) << SSFC_DBC_OFF) &
+ SSFC_DBC);
+ temp32 |= datatemp;
+ }
+
+ /* Select opcode */
+ opmenu = REGREAD32(ICH9_REG_OPMENU);
+ opmenu |= ((uint64_t)REGREAD32(ICH9_REG_OPMENU + 4)) << 32;
+
+ for (opcode_index = 0; opcode_index < 8; opcode_index++) {
+ if ((opmenu & 0xff) == op.opcode) {
+ break;
+ }
+ opmenu >>= 8;
+ }
+ if (opcode_index == 8) {
+ msg_pdbg("Opcode %x not found.\n", op.opcode);
+ return 1;
+ }
+ temp32 |= ((uint32_t) (opcode_index & 0x07)) << (8 + 4);
+
+ timeout = 100 * 60; /* 60 ms are 9.6 million cycles at 16 MHz. */
+ /* Handle Atomic. Atomic commands include three steps:
+ - sending the preop (mainly EWSR or WREN)
+ - sending the main command
+ - waiting for the busy bit (WIP) to be cleared
+ This means the timeout must be sufficient for chip erase
+ of slow high-capacity chips.
+ */
+ switch (op.atomic) {
+ case 2:
+ /* Select second preop. */
+ temp32 |= SSFC_SPOP;
+ /* And fall through. */
+ case 1:
+ /* Atomic command (preop+op) */
+ temp32 |= SSFC_ACS;
+ timeout = 100 * 1000 * 60; /* 60 seconds */
+ break;
+ }
+
+ /* Start */
+ temp32 |= SSFC_SCGO;
+
+ /* write it */
+ REGWRITE32(ICH9_REG_SSFS, temp32);
+
+ /* Wait for Cycle Done Status or Flash Cycle Error. */
+ while (((REGREAD32(ICH9_REG_SSFS) & (SSFS_FDONE | SSFS_FCERR)) == 0) &&
+ --timeout) {
+ programmer_delay(10);
+ }
+ if (!timeout) {
+ msg_perr("timeout, ICH9_REG_SSFS=0x%08x\n",
+ REGREAD32(ICH9_REG_SSFS));
+ return 1;
+ }
+
+ /* FIXME make sure we do not needlessly cause transaction errors. */
+ temp32 = REGREAD32(ICH9_REG_SSFS);
+ if (temp32 & SSFS_FCERR) {
+ msg_perr("Transaction error!\n");
+ prettyprint_ich9_reg_ssfs(temp32);
+ prettyprint_ich9_reg_ssfc(temp32);
+ /* keep reserved bits */
+ temp32 &= SSFS_RESERVED_MASK | SSFC_RESERVED_MASK;
+ /* Clear the transaction error. */
+ REGWRITE32(ICH9_REG_SSFS, temp32 | SSFS_FCERR);
+ return 1;
+ }
+
+ if ((!write_cmd) && (datalength != 0))
+ ich_read_data(data, datalength, ICH9_REG_FDATA0);
+
+ return 0;
+}
+
+static int run_opcode(const struct flashctx *flash, OPCODE op, uint32_t offset,
+ uint8_t datalength, uint8_t * data)
+{
+ /* max_data_read == max_data_write for all Intel/VIA SPI masters */
+ uint8_t maxlength = flash->mst->spi.max_data_read;
+
+ if (ich_generation == CHIPSET_ICH_UNKNOWN) {
+ msg_perr("%s: unsupported chipset\n", __func__);
+ return -1;
+ }
+
+ if (datalength > maxlength) {
+ msg_perr("%s: Internal command size error for "
+ "opcode 0x%02x, got datalength=%i, want <=%i\n",
+ __func__, op.opcode, datalength, maxlength);
+ return SPI_INVALID_LENGTH;
+ }
+
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ return ich7_run_opcode(op, offset, datalength, data, maxlength);
+ case CHIPSET_ICH8:
+ default: /* Future version might behave the same */
+ return ich9_run_opcode(op, offset, datalength, data);
+ }
+}
+
+static int ich_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int result;
+ int opcode_index = -1;
+ const unsigned char cmd = *writearr;
+ OPCODE *opcode;
+ uint32_t addr = 0;
+ uint8_t *data;
+ int count;
+
+ /* find cmd in opcodes-table */
+ opcode_index = find_opcode(curopcodes, cmd);
+ if (opcode_index == -1) {
+ if (!ichspi_lock)
+ opcode_index = reprogram_opcode_on_the_fly(cmd, writecnt, readcnt);
+ if (opcode_index == SPI_INVALID_LENGTH) {
+ msg_pdbg("OPCODE 0x%02x has unsupported length, will not execute.\n", cmd);
+ return SPI_INVALID_LENGTH;
+ } else if (opcode_index == -1) {
+ msg_pdbg("Invalid OPCODE 0x%02x, will not execute.\n",
+ cmd);
+ return SPI_INVALID_OPCODE;
+ }
+ }
+
+ opcode = &(curopcodes->opcode[opcode_index]);
+
+ /* The following valid writecnt/readcnt combinations exist:
+ * writecnt = 4, readcnt >= 0
+ * writecnt = 1, readcnt >= 0
+ * writecnt >= 4, readcnt = 0
+ * writecnt >= 1, readcnt = 0
+ * writecnt >= 1 is guaranteed for all commands.
+ */
+ if ((opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS) &&
+ (writecnt != 4)) {
+ msg_perr("%s: Internal command size error for opcode "
+ "0x%02x, got writecnt=%i, want =4\n", __func__, cmd,
+ writecnt);
+ return SPI_INVALID_LENGTH;
+ }
+ if ((opcode->spi_type == SPI_OPCODE_TYPE_READ_NO_ADDRESS) &&
+ (writecnt != 1)) {
+ msg_perr("%s: Internal command size error for opcode "
+ "0x%02x, got writecnt=%i, want =1\n", __func__, cmd,
+ writecnt);
+ return SPI_INVALID_LENGTH;
+ }
+ if ((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) &&
+ (writecnt < 4)) {
+ msg_perr("%s: Internal command size error for opcode "
+ "0x%02x, got writecnt=%i, want >=4\n", __func__, cmd,
+ writecnt);
+ return SPI_INVALID_LENGTH;
+ }
+ if (((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) ||
+ (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)) &&
+ (readcnt)) {
+ msg_perr("%s: Internal command size error for opcode "
+ "0x%02x, got readcnt=%i, want =0\n", __func__, cmd,
+ readcnt);
+ return SPI_INVALID_LENGTH;
+ }
+
+ /* if opcode-type requires an address */
+ if (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS ||
+ opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+ addr = (writearr[1] << 16) |
+ (writearr[2] << 8) | (writearr[3] << 0);
+ if (addr < ichspi_bbar) {
+ msg_perr("%s: Address 0x%06x below allowed "
+ "range 0x%06x-0xffffff\n", __func__,
+ addr, ichspi_bbar);
+ return SPI_INVALID_ADDRESS;
+ }
+ }
+
+ /* Translate read/write array/count.
+ * The maximum data length is identical for the maximum read length and
+ * for the maximum write length excluding opcode and address. Opcode and
+ * address are stored in separate registers, not in the data registers
+ * and are thus not counted towards data length. The only exception
+ * applies if the opcode definition (un)intentionally classifies said
+ * opcode incorrectly as non-address opcode or vice versa. */
+ if (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS) {
+ data = (uint8_t *) (writearr + 1);
+ count = writecnt - 1;
+ } else if (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) {
+ data = (uint8_t *) (writearr + 4);
+ count = writecnt - 4;
+ } else {
+ data = (uint8_t *) readarr;
+ count = readcnt;
+ }
+
+ result = run_opcode(flash, *opcode, addr, count, data);
+ if (result) {
+ msg_pdbg("Running OPCODE 0x%02x failed ", opcode->opcode);
+ if ((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) ||
+ (opcode->spi_type == SPI_OPCODE_TYPE_READ_WITH_ADDRESS)) {
+ msg_pdbg("at address 0x%06x ", addr);
+ }
+ msg_pdbg("(payload length was %d).\n", count);
+
+ /* Print out the data array if it contains data to write.
+ * Errors are detected before the received data is read back into
+ * the array so it won't make sense to print it then. */
+ if ((opcode->spi_type == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS) ||
+ (opcode->spi_type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS)) {
+ int i;
+ msg_pspew("The data was:\n");
+ for (i = 0; i < count; i++){
+ msg_pspew("%3d: 0x%02x\n", i, data[i]);
+ }
+ }
+ }
+
+ return result;
+}
+
+static struct hwseq_data {
+ uint32_t size_comp0;
+ uint32_t size_comp1;
+} hwseq_data;
+
+/* Sets FLA in FADDR to (addr & 0x01FFFFFF) without touching other bits. */
+static void ich_hwseq_set_addr(uint32_t addr)
+{
+ uint32_t addr_old = REGREAD32(ICH9_REG_FADDR) & ~0x01FFFFFF;
+ REGWRITE32(ICH9_REG_FADDR, (addr & 0x01FFFFFF) | addr_old);
+}
+
+/* Sets FADDR.FLA to 'addr' and returns the erase block size in bytes
+ * of the block containing this address. May return nonsense if the address is
+ * not valid. The erase block size for a specific address depends on the flash
+ * partition layout as specified by FPB and the partition properties as defined
+ * by UVSCC and LVSCC respectively. An alternative to implement this method
+ * would be by querying FPB and the respective VSCC register directly.
+ */
+static uint32_t ich_hwseq_get_erase_block_size(unsigned int addr)
+{
+ uint8_t enc_berase;
+ static const uint32_t dec_berase[4] = {
+ 256,
+ 4 * 1024,
+ 8 * 1024,
+ 64 * 1024
+ };
+
+ ich_hwseq_set_addr(addr);
+ enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >>
+ HSFS_BERASE_OFF;
+ return dec_berase[enc_berase];
+}
+
+/* Polls for Cycle Done Status, Flash Cycle Error or timeout in 8 us intervals.
+ Resets all error flags in HSFS.
+ Returns 0 if the cycle completes successfully without errors within
+ timeout us, 1 on errors. */
+static int ich_hwseq_wait_for_cycle_complete(unsigned int timeout,
+ unsigned int len)
+{
+ uint16_t hsfs;
+ uint32_t addr;
+
+ timeout /= 8; /* scale timeout duration to counter */
+ while ((((hsfs = REGREAD16(ICH9_REG_HSFS)) &
+ (HSFS_FDONE | HSFS_FCERR)) == 0) &&
+ --timeout) {
+ programmer_delay(8);
+ }
+ REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
+ if (!timeout) {
+ addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
+ msg_perr("Timeout error between offset 0x%08x and "
+ "0x%08x (= 0x%08x + %d)!\n",
+ addr, addr + len - 1, addr, len - 1);
+ prettyprint_ich9_reg_hsfs(hsfs);
+ prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
+ return 1;
+ }
+
+ if (hsfs & HSFS_FCERR) {
+ addr = REGREAD32(ICH9_REG_FADDR) & 0x01FFFFFF;
+ msg_perr("Transaction error between offset 0x%08x and "
+ "0x%08x (= 0x%08x + %d)!\n",
+ addr, addr + len - 1, addr, len - 1);
+ prettyprint_ich9_reg_hsfs(hsfs);
+ prettyprint_ich9_reg_hsfc(REGREAD16(ICH9_REG_HSFC));
+ return 1;
+ }
+ return 0;
+}
+
+static int ich_hwseq_probe(struct flashctx *flash)
+{
+ uint32_t total_size, boundary;
+ uint32_t erase_size_low, size_low, erase_size_high, size_high;
+ struct block_eraser *eraser;
+
+ total_size = hwseq_data.size_comp0 + hwseq_data.size_comp1;
+ msg_cdbg("Hardware sequencing reports %d attached SPI flash chip",
+ (hwseq_data.size_comp1 != 0) ? 2 : 1);
+ if (hwseq_data.size_comp1 != 0)
+ msg_cdbg("s with a combined");
+ else
+ msg_cdbg(" with a");
+ msg_cdbg(" density of %d kB.\n", total_size / 1024);
+ flash->chip->total_size = total_size / 1024;
+
+ eraser = &(flash->chip->block_erasers[0]);
+ boundary = (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) << 12;
+ size_high = total_size - boundary;
+ erase_size_high = ich_hwseq_get_erase_block_size(boundary);
+
+ if (boundary == 0) {
+ msg_cdbg2("There is only one partition containing the whole "
+ "address space (0x%06x - 0x%06x).\n", 0, size_high-1);
+ eraser->eraseblocks[0].size = erase_size_high;
+ eraser->eraseblocks[0].count = size_high / erase_size_high;
+ msg_cdbg2("There are %d erase blocks with %d B each.\n",
+ size_high / erase_size_high, erase_size_high);
+ } else {
+ msg_cdbg2("The flash address space (0x%06x - 0x%06x) is divided "
+ "at address 0x%06x in two partitions.\n",
+ 0, total_size-1, boundary);
+ size_low = total_size - size_high;
+ erase_size_low = ich_hwseq_get_erase_block_size(0);
+
+ eraser->eraseblocks[0].size = erase_size_low;
+ eraser->eraseblocks[0].count = size_low / erase_size_low;
+ msg_cdbg("The first partition ranges from 0x%06x to 0x%06x.\n",
+ 0, size_low-1);
+ msg_cdbg("In that range are %d erase blocks with %d B each.\n",
+ size_low / erase_size_low, erase_size_low);
+
+ eraser->eraseblocks[1].size = erase_size_high;
+ eraser->eraseblocks[1].count = size_high / erase_size_high;
+ msg_cdbg("The second partition ranges from 0x%06x to 0x%06x.\n",
+ boundary, total_size-1);
+ msg_cdbg("In that range are %d erase blocks with %d B each.\n",
+ size_high / erase_size_high, erase_size_high);
+ }
+ flash->chip->tested = TEST_OK_PREW;
+ return 1;
+}
+
+static int ich_hwseq_block_erase(struct flashctx *flash, unsigned int addr,
+ unsigned int len)
+{
+ uint32_t erase_block;
+ uint16_t hsfc;
+ uint32_t timeout = 5000 * 1000; /* 5 s for max 64 kB */
+
+ erase_block = ich_hwseq_get_erase_block_size(addr);
+ if (len != erase_block) {
+ msg_cerr("Erase block size for address 0x%06x is %d B, "
+ "but requested erase block size is %d B. "
+ "Not erasing anything.\n", addr, erase_block, len);
+ return -1;
+ }
+
+ /* Although the hardware supports this (it would erase the whole block
+ * containing the address) we play safe here. */
+ if (addr % erase_block != 0) {
+ msg_cerr("Erase address 0x%06x is not aligned to the erase "
+ "block boundary (any multiple of %d). "
+ "Not erasing anything.\n", addr, erase_block);
+ return -1;
+ }
+
+ if (addr + len > flash->chip->total_size * 1024) {
+ msg_perr("Request to erase some inaccessible memory address(es)"
+ " (addr=0x%x, len=%d). "
+ "Not erasing anything.\n", addr, len);
+ return -1;
+ }
+
+ msg_pdbg("Erasing %d bytes starting at 0x%06x.\n", len, addr);
+ ich_hwseq_set_addr(addr);
+
+ /* make sure FDONE, FCERR, AEL are cleared by writing 1 to them */
+ REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
+
+ hsfc = REGREAD16(ICH9_REG_HSFC);
+ hsfc &= ~HSFC_FCYCLE; /* clear operation */
+ hsfc |= (0x3 << HSFC_FCYCLE_OFF); /* set erase operation */
+ hsfc |= HSFC_FGO; /* start */
+ msg_pdbg("HSFC used for block erasing: ");
+ prettyprint_ich9_reg_hsfc(hsfc);
+ REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+ if (ich_hwseq_wait_for_cycle_complete(timeout, len))
+ return -1;
+ return 0;
+}
+
+static int ich_hwseq_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int addr, unsigned int len)
+{
+ uint16_t hsfc;
+ uint16_t timeout = 100 * 60;
+ uint8_t block_len;
+
+ if (addr + len > flash->chip->total_size * 1024) {
+ msg_perr("Request to read from an inaccessible memory address "
+ "(addr=0x%x, len=%d).\n", addr, len);
+ return -1;
+ }
+
+ msg_pdbg("Reading %d bytes starting at 0x%06x.\n", len, addr);
+ /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */
+ REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
+
+ while (len > 0) {
+ /* Obey programmer limit... */
+ block_len = min(len, flash->mst->opaque.max_data_read);
+ /* as well as flash chip page borders as demanded in the Intel datasheets. */
+ block_len = min(block_len, 256 - (addr & 0xFF));
+
+ ich_hwseq_set_addr(addr);
+ hsfc = REGREAD16(ICH9_REG_HSFC);
+ hsfc &= ~HSFC_FCYCLE; /* set read operation */
+ hsfc &= ~HSFC_FDBC; /* clear byte count */
+ /* set byte count */
+ hsfc |= (((block_len - 1) << HSFC_FDBC_OFF) & HSFC_FDBC);
+ hsfc |= HSFC_FGO; /* start */
+ REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+ if (ich_hwseq_wait_for_cycle_complete(timeout, block_len))
+ return 1;
+ ich_read_data(buf, block_len, ICH9_REG_FDATA0);
+ addr += block_len;
+ buf += block_len;
+ len -= block_len;
+ }
+ return 0;
+}
+
+static int ich_hwseq_write(struct flashctx *flash, const uint8_t *buf, unsigned int addr, unsigned int len)
+{
+ uint16_t hsfc;
+ uint16_t timeout = 100 * 60;
+ uint8_t block_len;
+
+ if (addr + len > flash->chip->total_size * 1024) {
+ msg_perr("Request to write to an inaccessible memory address "
+ "(addr=0x%x, len=%d).\n", addr, len);
+ return -1;
+ }
+
+ msg_pdbg("Writing %d bytes starting at 0x%06x.\n", len, addr);
+ /* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */
+ REGWRITE16(ICH9_REG_HSFS, REGREAD16(ICH9_REG_HSFS));
+
+ while (len > 0) {
+ ich_hwseq_set_addr(addr);
+ /* Obey programmer limit... */
+ block_len = min(len, flash->mst->opaque.max_data_write);
+ /* as well as flash chip page borders as demanded in the Intel datasheets. */
+ block_len = min(block_len, 256 - (addr & 0xFF));
+ ich_fill_data(buf, block_len, ICH9_REG_FDATA0);
+ hsfc = REGREAD16(ICH9_REG_HSFC);
+ hsfc &= ~HSFC_FCYCLE; /* clear operation */
+ hsfc |= (0x2 << HSFC_FCYCLE_OFF); /* set write operation */
+ hsfc &= ~HSFC_FDBC; /* clear byte count */
+ /* set byte count */
+ hsfc |= (((block_len - 1) << HSFC_FDBC_OFF) & HSFC_FDBC);
+ hsfc |= HSFC_FGO; /* start */
+ REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+ if (ich_hwseq_wait_for_cycle_complete(timeout, block_len))
+ return -1;
+ addr += block_len;
+ buf += block_len;
+ len -= block_len;
+ }
+ return 0;
+}
+
+static int ich_spi_send_multicommand(struct flashctx *flash,
+ struct spi_command *cmds)
+{
+ int ret = 0;
+ int i;
+ int oppos, preoppos;
+ for (; (cmds->writecnt || cmds->readcnt) && !ret; cmds++) {
+ if ((cmds + 1)->writecnt || (cmds + 1)->readcnt) {
+ /* Next command is valid. */
+ preoppos = find_preop(curopcodes, cmds->writearr[0]);
+ oppos = find_opcode(curopcodes, (cmds + 1)->writearr[0]);
+ if ((oppos == -1) && (preoppos != -1)) {
+ /* Current command is listed as preopcode in
+ * ICH struct OPCODES, but next command is not
+ * listed as opcode in that struct.
+ * Check for command sanity, then
+ * try to reprogram the ICH opcode list.
+ */
+ if (find_preop(curopcodes,
+ (cmds + 1)->writearr[0]) != -1) {
+ msg_perr("%s: Two subsequent "
+ "preopcodes 0x%02x and 0x%02x, "
+ "ignoring the first.\n",
+ __func__, cmds->writearr[0],
+ (cmds + 1)->writearr[0]);
+ continue;
+ }
+ /* If the chipset is locked down, we'll fail
+ * during execution of the next command anyway.
+ * No need to bother with fixups.
+ */
+ if (!ichspi_lock) {
+ oppos = reprogram_opcode_on_the_fly((cmds + 1)->writearr[0], (cmds + 1)->writecnt, (cmds + 1)->readcnt);
+ if (oppos == -1)
+ continue;
+ curopcodes->opcode[oppos].atomic = preoppos + 1;
+ continue;
+ }
+ }
+ if ((oppos != -1) && (preoppos != -1)) {
+ /* Current command is listed as preopcode in
+ * ICH struct OPCODES and next command is listed
+ * as opcode in that struct. Match them up.
+ */
+ curopcodes->opcode[oppos].atomic = preoppos + 1;
+ continue;
+ }
+ /* If none of the above if-statements about oppos or
+ * preoppos matched, this is a normal opcode.
+ */
+ }
+ ret = ich_spi_send_command(flash, cmds->writecnt, cmds->readcnt,
+ cmds->writearr, cmds->readarr);
+ /* Reset the type of all opcodes to non-atomic. */
+ for (i = 0; i < 8; i++)
+ curopcodes->opcode[i].atomic = 0;
+ }
+ return ret;
+}
+
+#define ICH_BMWAG(x) ((x >> 24) & 0xff)
+#define ICH_BMRAG(x) ((x >> 16) & 0xff)
+#define ICH_BRWA(x) ((x >> 8) & 0xff)
+#define ICH_BRRA(x) ((x >> 0) & 0xff)
+
+/* returns 0 if region is unused or r/w */
+static int ich9_handle_frap(uint32_t frap, int i)
+{
+ static const char *const access_names[4] = {
+ "locked", "read-only", "write-only", "read-write"
+ };
+ static const char *const region_names[5] = {
+ "Flash Descriptor", "BIOS", "Management Engine",
+ "Gigabit Ethernet", "Platform Data"
+ };
+ uint32_t base, limit;
+ int rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) |
+ (((ICH_BRRA(frap) >> i) & 1) << 0);
+ int offset = ICH9_REG_FREG0 + i * 4;
+ uint32_t freg = mmio_readl(ich_spibar + offset);
+
+ base = ICH_FREG_BASE(freg);
+ limit = ICH_FREG_LIMIT(freg);
+ if (base > limit || (freg == 0 && i > 0)) {
+ /* this FREG is disabled */
+ msg_pdbg2("0x%02X: 0x%08x FREG%i: %s region is unused.\n",
+ offset, freg, i, region_names[i]);
+ return 0;
+ }
+ msg_pdbg("0x%02X: 0x%08x ", offset, freg);
+ if (rwperms == 0x3) {
+ msg_pdbg("FREG%i: %s region (0x%08x-0x%08x) is %s.\n", i,
+ region_names[i], base, (limit | 0x0fff),
+ access_names[rwperms]);
+ return 0;
+ }
+
+ msg_pwarn("FREG%i: Warning: %s region (0x%08x-0x%08x) is %s.\n", i,
+ region_names[i], base, (limit | 0x0fff),
+ access_names[rwperms]);
+ return 1;
+}
+
+ /* In contrast to FRAP and the master section of the descriptor the bits
+ * in the PR registers have an inverted meaning. The bits in FRAP
+ * indicate read and write access _grant_. Here they indicate read
+ * and write _protection_ respectively. If both bits are 0 the address
+ * bits are ignored.
+ */
+#define ICH_PR_PERMS(pr) (((~((pr) >> PR_RP_OFF) & 1) << 0) | \
+ ((~((pr) >> PR_WP_OFF) & 1) << 1))
+
+/* returns 0 if range is unused (i.e. r/w) */
+static int ich9_handle_pr(int i)
+{
+ static const char *const access_names[3] = {
+ "locked", "read-only", "write-only"
+ };
+ uint8_t off = ICH9_REG_PR0 + (i * 4);
+ uint32_t pr = mmio_readl(ich_spibar + off);
+ unsigned int rwperms = ICH_PR_PERMS(pr);
+
+ if (rwperms == 0x3) {
+ msg_pdbg2("0x%02X: 0x%08x (PR%u is unused)\n", off, pr, i);
+ return 0;
+ }
+
+ msg_pdbg("0x%02X: 0x%08x ", off, pr);
+ msg_pwarn("PR%u: Warning: 0x%08x-0x%08x is %s.\n", i, ICH_FREG_BASE(pr),
+ ICH_FREG_LIMIT(pr) | 0x0fff, access_names[rwperms]);
+ return 1;
+}
+
+/* Set/Clear the read and write protection enable bits of PR register @i
+ * according to @read_prot and @write_prot. */
+static void ich9_set_pr(int i, int read_prot, int write_prot)
+{
+ void *addr = ich_spibar + ICH9_REG_PR0 + (i * 4);
+ uint32_t old = mmio_readl(addr);
+ uint32_t new;
+
+ msg_gspew("PR%u is 0x%08x", i, old);
+ new = old & ~((1 << PR_RP_OFF) | (1 << PR_WP_OFF));
+ if (read_prot)
+ new |= (1 << PR_RP_OFF);
+ if (write_prot)
+ new |= (1 << PR_WP_OFF);
+ if (old == new) {
+ msg_gspew(" already.\n");
+ return;
+ }
+ msg_gspew(", trying to set it to 0x%08x ", new);
+ rmmio_writel(new, addr);
+ msg_gspew("resulted in 0x%08x.\n", mmio_readl(addr));
+}
+
+static const struct spi_master spi_master_ich7 = {
+ .type = SPI_CONTROLLER_ICH7,
+ .max_data_read = 64,
+ .max_data_write = 64,
+ .command = ich_spi_send_command,
+ .multicommand = ich_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static const struct spi_master spi_master_ich9 = {
+ .type = SPI_CONTROLLER_ICH9,
+ .max_data_read = 64,
+ .max_data_write = 64,
+ .command = ich_spi_send_command,
+ .multicommand = ich_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static const struct opaque_master opaque_master_ich_hwseq = {
+ .max_data_read = 64,
+ .max_data_write = 64,
+ .probe = ich_hwseq_probe,
+ .read = ich_hwseq_read,
+ .write = ich_hwseq_write,
+ .erase = ich_hwseq_block_erase,
+};
+
+int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_gen)
+{
+ int i;
+ uint16_t tmp2;
+ uint32_t tmp;
+ char *arg;
+ int ich_spi_force = 0;
+ int ich_spi_rw_restricted = 0;
+ int desc_valid = 0;
+ struct ich_descriptors desc = {{ 0 }};
+ enum ich_spi_mode {
+ ich_auto,
+ ich_hwseq,
+ ich_swseq
+ } ich_spi_mode = ich_auto;
+
+ ich_generation = ich_gen;
+ ich_spibar = spibar;
+
+ switch (ich_generation) {
+ case CHIPSET_ICH7:
+ case CHIPSET_TUNNEL_CREEK:
+ case CHIPSET_CENTERTON:
+ msg_pdbg("0x00: 0x%04x (SPIS)\n",
+ mmio_readw(ich_spibar + 0));
+ msg_pdbg("0x02: 0x%04x (SPIC)\n",
+ mmio_readw(ich_spibar + 2));
+ msg_pdbg("0x04: 0x%08x (SPIA)\n",
+ mmio_readl(ich_spibar + 4));
+ ichspi_bbar = mmio_readl(ich_spibar + 0x50);
+ msg_pdbg("0x50: 0x%08x (BBAR)\n",
+ ichspi_bbar);
+ msg_pdbg("0x54: 0x%04x (PREOP)\n",
+ mmio_readw(ich_spibar + 0x54));
+ msg_pdbg("0x56: 0x%04x (OPTYPE)\n",
+ mmio_readw(ich_spibar + 0x56));
+ msg_pdbg("0x58: 0x%08x (OPMENU)\n",
+ mmio_readl(ich_spibar + 0x58));
+ msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n",
+ mmio_readl(ich_spibar + 0x5c));
+ for (i = 0; i < 3; i++) {
+ int offs;
+ offs = 0x60 + (i * 4);
+ msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs,
+ mmio_readl(ich_spibar + offs), i);
+ }
+ if (mmio_readw(ich_spibar) & (1 << 15)) {
+ msg_pwarn("WARNING: SPI Configuration Lockdown activated.\n");
+ ichspi_lock = 1;
+ }
+ ich_init_opcodes();
+ ich_set_bbar(0);
+ register_spi_master(&spi_master_ich7);
+ break;
+ case CHIPSET_ICH8:
+ default: /* Future version might behave the same */
+ arg = extract_programmer_param("ich_spi_mode");
+ if (arg && !strcmp(arg, "hwseq")) {
+ ich_spi_mode = ich_hwseq;
+ msg_pspew("user selected hwseq\n");
+ } else if (arg && !strcmp(arg, "swseq")) {
+ ich_spi_mode = ich_swseq;
+ msg_pspew("user selected swseq\n");
+ } else if (arg && !strcmp(arg, "auto")) {
+ msg_pspew("user selected auto\n");
+ ich_spi_mode = ich_auto;
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for ich_spi_mode.\n");
+ free(arg);
+ return ERROR_FATAL;
+ } else if (arg) {
+ msg_perr("Unknown argument for ich_spi_mode: %s\n",
+ arg);
+ free(arg);
+ return ERROR_FATAL;
+ }
+ free(arg);
+
+ arg = extract_programmer_param("ich_spi_force");
+ if (arg && !strcmp(arg, "yes")) {
+ ich_spi_force = 1;
+ msg_pspew("ich_spi_force enabled.\n");
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for ich_spi_force.\n");
+ free(arg);
+ return ERROR_FATAL;
+ } else if (arg) {
+ msg_perr("Unknown argument for ich_spi_force: \"%s\" "
+ "(not \"yes\").\n", arg);
+ free(arg);
+ return ERROR_FATAL;
+ }
+ free(arg);
+
+ tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS);
+ msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
+ prettyprint_ich9_reg_hsfs(tmp2);
+ if (tmp2 & HSFS_FLOCKDN) {
+ msg_pwarn("Warning: SPI Configuration Lockdown activated.\n");
+ ichspi_lock = 1;
+ }
+ if (tmp2 & HSFS_FDV)
+ desc_valid = 1;
+ if (!(tmp2 & HSFS_FDOPSS) && desc_valid)
+ msg_pinfo("The Flash Descriptor Override Strap-Pin is set. Restrictions implied by\n"
+ "the Master Section of the flash descriptor are NOT in effect. Please note\n"
+ "that Protected Range (PR) restrictions still apply.\n");
+ ich_init_opcodes();
+
+ if (desc_valid) {
+ tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFC);
+ msg_pdbg("0x06: 0x%04x (HSFC)\n", tmp2);
+ prettyprint_ich9_reg_hsfc(tmp2);
+ }
+
+ tmp = mmio_readl(ich_spibar + ICH9_REG_FADDR);
+ msg_pdbg2("0x08: 0x%08x (FADDR)\n", tmp);
+
+ if (desc_valid) {
+ tmp = mmio_readl(ich_spibar + ICH9_REG_FRAP);
+ msg_pdbg("0x50: 0x%08x (FRAP)\n", tmp);
+ msg_pdbg("BMWAG 0x%02x, ", ICH_BMWAG(tmp));
+ msg_pdbg("BMRAG 0x%02x, ", ICH_BMRAG(tmp));
+ msg_pdbg("BRWA 0x%02x, ", ICH_BRWA(tmp));
+ msg_pdbg("BRRA 0x%02x\n", ICH_BRRA(tmp));
+
+ /* Handle FREGx and FRAP registers */
+ for (i = 0; i < 5; i++)
+ ich_spi_rw_restricted |= ich9_handle_frap(tmp, i);
+ if (ich_spi_rw_restricted)
+ msg_pwarn("Not all flash regions are freely accessible by flashrom. This is "
+ "most likely\ndue to an active ME. Please see "
+ "https://flashrom.org/ME for details.\n");
+ }
+
+ /* Handle PR registers */
+ for (i = 0; i < 5; i++) {
+ /* if not locked down try to disable PR locks first */
+ if (!ichspi_lock)
+ ich9_set_pr(i, 0, 0);
+ ich_spi_rw_restricted |= ich9_handle_pr(i);
+ }
+
+ if (ich_spi_rw_restricted) {
+ if (!ich_spi_force)
+ programmer_may_write = 0;
+ msg_pinfo("Writes have been disabled for safety reasons. You can enforce write\n"
+ "support with the ich_spi_force programmer option, but you will most likely\n"
+ "harm your hardware! If you force flashrom you will get no support if\n"
+ "something breaks. On a few mainboards it is possible to enable write\n"
+ "access by setting a jumper (see its documentation or the board itself).\n");
+ if (ich_spi_force)
+ msg_pinfo("Continuing with write support because the user forced us to!\n");
+ }
+
+ tmp = mmio_readl(ich_spibar + ICH9_REG_SSFS);
+ msg_pdbg("0x90: 0x%02x (SSFS)\n", tmp & 0xff);
+ prettyprint_ich9_reg_ssfs(tmp);
+ if (tmp & SSFS_FCERR) {
+ msg_pdbg("Clearing SSFS.FCERR\n");
+ mmio_writeb(SSFS_FCERR, ich_spibar + ICH9_REG_SSFS);
+ }
+ msg_pdbg("0x91: 0x%06x (SSFC)\n", tmp >> 8);
+ prettyprint_ich9_reg_ssfc(tmp);
+
+ msg_pdbg("0x94: 0x%04x (PREOP)\n",
+ mmio_readw(ich_spibar + ICH9_REG_PREOP));
+ msg_pdbg("0x96: 0x%04x (OPTYPE)\n",
+ mmio_readw(ich_spibar + ICH9_REG_OPTYPE));
+ msg_pdbg("0x98: 0x%08x (OPMENU)\n",
+ mmio_readl(ich_spibar + ICH9_REG_OPMENU));
+ msg_pdbg("0x9C: 0x%08x (OPMENU+4)\n",
+ mmio_readl(ich_spibar + ICH9_REG_OPMENU + 4));
+ if (ich_generation == CHIPSET_ICH8 && desc_valid) {
+ tmp = mmio_readl(ich_spibar + ICH8_REG_VSCC);
+ msg_pdbg("0xC1: 0x%08x (VSCC)\n", tmp);
+ msg_pdbg("VSCC: ");
+ prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, true);
+ } else {
+ if (ich_generation != CHIPSET_BAYTRAIL && desc_valid) {
+ ichspi_bbar = mmio_readl(ich_spibar + ICH9_REG_BBAR);
+ msg_pdbg("0xA0: 0x%08x (BBAR)\n",
+ ichspi_bbar);
+ ich_set_bbar(0);
+ }
+
+ if (desc_valid) {
+ tmp = mmio_readl(ich_spibar + ICH9_REG_LVSCC);
+ msg_pdbg("0xC4: 0x%08x (LVSCC)\n", tmp);
+ msg_pdbg("LVSCC: ");
+ prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, true);
+
+ tmp = mmio_readl(ich_spibar + ICH9_REG_UVSCC);
+ msg_pdbg("0xC8: 0x%08x (UVSCC)\n", tmp);
+ msg_pdbg("UVSCC: ");
+ prettyprint_ich_reg_vscc(tmp, MSG_DEBUG, false);
+
+ tmp = mmio_readl(ich_spibar + ICH9_REG_FPB);
+ msg_pdbg("0xD0: 0x%08x (FPB)\n", tmp);
+ }
+ }
+
+ if (desc_valid) {
+ if (read_ich_descriptors_via_fdo(ich_spibar, &desc) == ICH_RET_OK)
+ prettyprint_ich_descriptors(ich_gen, &desc);
+
+ /* If the descriptor is valid and indicates multiple
+ * flash devices we need to use hwseq to be able to
+ * access the second flash device.
+ */
+ if (ich_spi_mode == ich_auto && desc.content.NC != 0) {
+ msg_pinfo("Enabling hardware sequencing due to "
+ "multiple flash chips detected.\n");
+ ich_spi_mode = ich_hwseq;
+ }
+ }
+
+ if (ich_spi_mode == ich_auto && ichspi_lock &&
+ ich_missing_opcodes()) {
+ msg_pinfo("Enabling hardware sequencing because "
+ "some important opcode is locked.\n");
+ ich_spi_mode = ich_hwseq;
+ }
+
+ if (ich_spi_mode == ich_hwseq) {
+ if (!desc_valid) {
+ msg_perr("Hardware sequencing was requested "
+ "but the flash descriptor is not "
+ "valid. Aborting.\n");
+ return ERROR_FATAL;
+ }
+
+ int tmpi = getFCBA_component_density(ich_generation, &desc, 0);
+ if (tmpi < 0) {
+ msg_perr("Could not determine density of flash component %d.\n", 0);
+ return ERROR_FATAL;
+ }
+ hwseq_data.size_comp0 = tmpi;
+
+ tmpi = getFCBA_component_density(ich_generation, &desc, 1);
+ if (tmpi < 0) {
+ msg_perr("Could not determine density of flash component %d.\n", 1);
+ return ERROR_FATAL;
+ }
+ hwseq_data.size_comp1 = tmpi;
+
+ register_opaque_master(&opaque_master_ich_hwseq);
+ } else {
+ register_spi_master(&spi_master_ich9);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const struct spi_master spi_master_via = {
+ .type = SPI_CONTROLLER_VIA,
+ .max_data_read = 16,
+ .max_data_write = 16,
+ .command = ich_spi_send_command,
+ .multicommand = ich_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+int via_init_spi(struct pci_dev *dev, uint32_t mmio_base)
+{
+ int i;
+
+ ich_spibar = rphysmap("VIA SPI MMIO registers", mmio_base, 0x70);
+ if (ich_spibar == ERROR_PTR)
+ return ERROR_FATAL;
+ /* Do we really need no write enable? Like the LPC one at D17F0 0x40 */
+
+ /* Not sure if it speaks all these bus protocols. */
+ internal_buses_supported = BUS_LPC | BUS_FWH;
+ ich_generation = CHIPSET_ICH7;
+ register_spi_master(&spi_master_via);
+
+ msg_pdbg("0x00: 0x%04x (SPIS)\n", mmio_readw(ich_spibar + 0));
+ msg_pdbg("0x02: 0x%04x (SPIC)\n", mmio_readw(ich_spibar + 2));
+ msg_pdbg("0x04: 0x%08x (SPIA)\n", mmio_readl(ich_spibar + 4));
+ for (i = 0; i < 2; i++) {
+ int offs;
+ offs = 8 + (i * 8);
+ msg_pdbg("0x%02x: 0x%08x (SPID%d)\n", offs,
+ mmio_readl(ich_spibar + offs), i);
+ msg_pdbg("0x%02x: 0x%08x (SPID%d+4)\n", offs + 4,
+ mmio_readl(ich_spibar + offs + 4), i);
+ }
+ ichspi_bbar = mmio_readl(ich_spibar + 0x50);
+ msg_pdbg("0x50: 0x%08x (BBAR)\n", ichspi_bbar);
+ msg_pdbg("0x54: 0x%04x (PREOP)\n", mmio_readw(ich_spibar + 0x54));
+ msg_pdbg("0x56: 0x%04x (OPTYPE)\n", mmio_readw(ich_spibar + 0x56));
+ msg_pdbg("0x58: 0x%08x (OPMENU)\n", mmio_readl(ich_spibar + 0x58));
+ msg_pdbg("0x5c: 0x%08x (OPMENU+4)\n", mmio_readl(ich_spibar + 0x5c));
+ for (i = 0; i < 3; i++) {
+ int offs;
+ offs = 0x60 + (i * 4);
+ msg_pdbg("0x%02x: 0x%08x (PBR%d)\n", offs,
+ mmio_readl(ich_spibar + offs), i);
+ }
+ msg_pdbg("0x6c: 0x%04x (CLOCK/DEBUG)\n",
+ mmio_readw(ich_spibar + 0x6c));
+ if (mmio_readw(ich_spibar) & (1 << 15)) {
+ msg_pwarn("Warning: SPI Configuration Lockdown activated.\n");
+ ichspi_lock = 1;
+ }
+
+ ich_set_bbar(0);
+ ich_init_opcodes();
+
+ return 0;
+}
+
+#endif
diff --git a/internal.c b/internal.c
new file mode 100644
index 0000000..4062b64
--- /dev/null
+++ b/internal.c
@@ -0,0 +1,402 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#if NEED_PCI == 1
+struct pci_dev *pci_dev_find_filter(struct pci_filter filter)
+{
+ struct pci_dev *temp;
+
+ for (temp = pacc->devices; temp; temp = temp->next)
+ if (pci_filter_match(&filter, temp))
+ return temp;
+
+ return NULL;
+}
+
+struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t devclass)
+{
+ struct pci_dev *temp;
+ struct pci_filter filter;
+ uint16_t tmp2;
+
+ pci_filter_init(NULL, &filter);
+ filter.vendor = vendor;
+
+ for (temp = pacc->devices; temp; temp = temp->next)
+ if (pci_filter_match(&filter, temp)) {
+ /* Read PCI class */
+ tmp2 = pci_read_word(temp, 0x0a);
+ if (tmp2 == devclass)
+ return temp;
+ }
+
+ return NULL;
+}
+
+struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
+{
+ struct pci_dev *temp;
+ struct pci_filter filter;
+
+ pci_filter_init(NULL, &filter);
+ filter.vendor = vendor;
+ filter.device = device;
+
+ for (temp = pacc->devices; temp; temp = temp->next)
+ if (pci_filter_match(&filter, temp))
+ return temp;
+
+ return NULL;
+}
+
+struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
+ uint16_t card_vendor, uint16_t card_device)
+{
+ struct pci_dev *temp;
+ struct pci_filter filter;
+
+ pci_filter_init(NULL, &filter);
+ filter.vendor = vendor;
+ filter.device = device;
+
+ for (temp = pacc->devices; temp; temp = temp->next)
+ if (pci_filter_match(&filter, temp)) {
+ if ((card_vendor ==
+ pci_read_word(temp, PCI_SUBSYSTEM_VENDOR_ID))
+ && (card_device ==
+ pci_read_word(temp, PCI_SUBSYSTEM_ID)))
+ return temp;
+ }
+
+ return NULL;
+}
+#endif
+
+#if CONFIG_INTERNAL == 1
+int force_boardenable = 0;
+int force_boardmismatch = 0;
+
+#if defined(__i386__) || defined(__x86_64__)
+void probe_superio(void)
+{
+ probe_superio_winbond();
+ /* ITE probe causes SMSC LPC47N217 to power off the serial UART.
+ * Always probe for SMSC first, and if a SMSC Super I/O is detected
+ * at a given I/O port, do _not_ probe that port with the ITE probe.
+ * This means SMSC probing must be done before ITE probing.
+ */
+ //probe_superio_smsc();
+ probe_superio_ite();
+}
+
+int superio_count = 0;
+#define SUPERIO_MAX_COUNT 3
+
+struct superio superios[SUPERIO_MAX_COUNT];
+
+int register_superio(struct superio s)
+{
+ if (superio_count == SUPERIO_MAX_COUNT)
+ return 1;
+ superios[superio_count++] = s;
+ return 0;
+}
+
+#endif
+
+int is_laptop = 0;
+int laptop_ok = 0;
+
+static void internal_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static void internal_chip_writew(const struct flashctx *flash, uint16_t val,
+ chipaddr addr);
+static void internal_chip_writel(const struct flashctx *flash, uint32_t val,
+ chipaddr addr);
+static uint8_t internal_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static uint16_t internal_chip_readw(const struct flashctx *flash,
+ const chipaddr addr);
+static uint32_t internal_chip_readl(const struct flashctx *flash,
+ const chipaddr addr);
+static void internal_chip_readn(const struct flashctx *flash, uint8_t *buf,
+ const chipaddr addr, size_t len);
+static const struct par_master par_master_internal = {
+ .chip_readb = internal_chip_readb,
+ .chip_readw = internal_chip_readw,
+ .chip_readl = internal_chip_readl,
+ .chip_readn = internal_chip_readn,
+ .chip_writeb = internal_chip_writeb,
+ .chip_writew = internal_chip_writew,
+ .chip_writel = internal_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+enum chipbustype internal_buses_supported = BUS_NONE;
+
+int internal_init(void)
+{
+#if defined __FLASHROM_LITTLE_ENDIAN__
+ int ret = 0;
+#endif
+ int force_laptop = 0;
+ int not_a_laptop = 0;
+ const char *board_vendor = NULL;
+ const char *board_model = NULL;
+#if IS_X86 || IS_ARM
+ const char *cb_vendor = NULL;
+ const char *cb_model = NULL;
+#endif
+ char *arg;
+
+ arg = extract_programmer_param("boardenable");
+ if (arg && !strcmp(arg,"force")) {
+ force_boardenable = 1;
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for boardenable.\n");
+ free(arg);
+ return 1;
+ } else if (arg) {
+ msg_perr("Unknown argument for boardenable: %s\n", arg);
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ arg = extract_programmer_param("boardmismatch");
+ if (arg && !strcmp(arg,"force")) {
+ force_boardmismatch = 1;
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for boardmismatch.\n");
+ free(arg);
+ return 1;
+ } else if (arg) {
+ msg_perr("Unknown argument for boardmismatch: %s\n", arg);
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ arg = extract_programmer_param("laptop");
+ if (arg && !strcmp(arg, "force_I_want_a_brick"))
+ force_laptop = 1;
+ else if (arg && !strcmp(arg, "this_is_not_a_laptop"))
+ not_a_laptop = 1;
+ else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for laptop.\n");
+ free(arg);
+ return 1;
+ } else if (arg) {
+ msg_perr("Unknown argument for laptop: %s\n", arg);
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ arg = extract_programmer_param("mainboard");
+ if (arg && strlen(arg)) {
+ if (board_parse_parameter(arg, &board_vendor, &board_model)) {
+ free(arg);
+ return 1;
+ }
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for mainboard.\n");
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ if (rget_io_perms())
+ return 1;
+
+ /* Default to Parallel/LPC/FWH flash devices. If a known host controller
+ * is found, the host controller init routine sets the
+ * internal_buses_supported bitfield.
+ */
+ internal_buses_supported = BUS_NONSPI;
+
+ /* Initialize PCI access for flash enables */
+ if (pci_init_common() != 0)
+ return 1;
+
+ if (processor_flash_enable()) {
+ msg_perr("Processor detection/init failed.\n"
+ "Aborting.\n");
+ return 1;
+ }
+
+#if IS_X86 || IS_ARM
+ if ((cb_parse_table(&cb_vendor, &cb_model) == 0) && (board_vendor != NULL) && (board_model != NULL)) {
+ if (strcasecmp(board_vendor, cb_vendor) || strcasecmp(board_model, cb_model)) {
+ msg_pwarn("Warning: The mainboard IDs set by -p internal:mainboard (%s:%s) do not\n"
+ " match the current coreboot IDs of the mainboard (%s:%s).\n",
+ board_vendor, board_model, cb_vendor, cb_model);
+ if (!force_boardmismatch)
+ return 1;
+ msg_pinfo("Continuing anyway.\n");
+ }
+ }
+#endif
+
+#if IS_X86
+ dmi_init();
+
+ /* In case Super I/O probing would cause pretty explosions. */
+ board_handle_before_superio();
+
+ /* Probe for the Super I/O chip and fill global struct superio. */
+ probe_superio();
+#else
+ /* FIXME: Enable cbtable searching on all non-x86 platforms supported
+ * by coreboot.
+ * FIXME: Find a replacement for DMI on non-x86.
+ * FIXME: Enable Super I/O probing once port I/O is possible.
+ */
+#endif
+
+ /* Check laptop whitelist. */
+ board_handle_before_laptop();
+
+ /* Warn if a non-whitelisted laptop is detected. */
+ if (is_laptop && !laptop_ok) {
+ msg_perr("========================================================================\n");
+ if (is_laptop == 1) {
+ msg_perr("WARNING! You seem to be running flashrom on an unsupported laptop.\n");
+ } else {
+ msg_perr("WARNING! You may be running flashrom on an unsupported laptop. We could\n"
+ "not detect this for sure because your vendor has not setup the SMBIOS\n"
+ "tables correctly. You can enforce execution by adding\n"
+ "'-p internal:laptop=this_is_not_a_laptop' to the command line, but\n"
+ "please read the following warning if you are not sure.\n\n");
+ }
+ msg_perr("Laptops, notebooks and netbooks are difficult to support and we\n"
+ "recommend to use the vendor flashing utility. The embedded controller\n"
+ "(EC) in these machines often interacts badly with flashing.\n"
+ "See the manpage and https://flashrom.org/Laptops for details.\n\n"
+ "If flash is shared with the EC, erase is guaranteed to brick your laptop\n"
+ "and write may brick your laptop.\n"
+ "Read and probe may irritate your EC and cause fan failure, backlight\n"
+ "failure and sudden poweroff.\n"
+ "You have been warned.\n"
+ "========================================================================\n");
+
+ if (force_laptop || (not_a_laptop && (is_laptop == 2))) {
+ msg_perr("Proceeding anyway because user forced us to.\n");
+ } else {
+ msg_perr("Aborting.\n");
+ return 1;
+ }
+ }
+
+#ifdef __FLASHROM_LITTLE_ENDIAN__
+ /* try to enable it. Failure IS an option, since not all motherboards
+ * really need this to be done, etc., etc.
+ */
+ ret = chipset_flash_enable();
+ if (ret == -2) {
+ msg_perr("WARNING: No chipset found. Flash detection "
+ "will most likely fail.\n");
+ } else if (ret == ERROR_FATAL)
+ return ret;
+
+#if IS_X86
+ /* Probe unconditionally for ITE Super I/O chips. This enables LPC->SPI translation on IT87* and
+ * parallel writes on IT8705F. Also, this handles the manual chip select for Gigabyte's DualBIOS. */
+ init_superio_ite();
+
+ if (board_flash_enable(board_vendor, board_model, cb_vendor, cb_model)) {
+ msg_perr("Aborting to be safe.\n");
+ return 1;
+ }
+#endif
+
+#if IS_X86 || IS_MIPS
+ register_par_master(&par_master_internal, internal_buses_supported);
+ return 0;
+#else
+ msg_perr("Your platform is not supported yet for the internal "
+ "programmer due to missing\n"
+ "flash_base and top/bottom alignment information.\n"
+ "Aborting.\n");
+ return 1;
+#endif
+#else
+ /* FIXME: Remove this unconditional abort once all PCI drivers are
+ * converted to use little-endian accesses for memory BARs.
+ */
+ msg_perr("Your platform is not supported yet for the internal "
+ "programmer because it has\n"
+ "not been converted from native endian to little endian "
+ "access yet.\n"
+ "Aborting.\n");
+ return 1;
+#endif
+}
+#endif
+
+static void internal_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ mmio_writeb(val, (void *) addr);
+}
+
+static void internal_chip_writew(const struct flashctx *flash, uint16_t val,
+ chipaddr addr)
+{
+ mmio_writew(val, (void *) addr);
+}
+
+static void internal_chip_writel(const struct flashctx *flash, uint32_t val,
+ chipaddr addr)
+{
+ mmio_writel(val, (void *) addr);
+}
+
+static uint8_t internal_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return mmio_readb((void *) addr);
+}
+
+static uint16_t internal_chip_readw(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return mmio_readw((void *) addr);
+}
+
+static uint32_t internal_chip_readl(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return mmio_readl((void *) addr);
+}
+
+static void internal_chip_readn(const struct flashctx *flash, uint8_t *buf,
+ const chipaddr addr, size_t len)
+{
+ mmio_readn((void *)addr, buf, len);
+ return;
+}
diff --git a/it8212.c b/it8212.c
new file mode 100644
index 0000000..460e820
--- /dev/null
+++ b/it8212.c
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Carl-Daniel Hailfinger
+ * Copyright (C) 2012 Kyösti Mälkki <kyosti.malkki@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+static uint8_t *it8212_bar = NULL;
+
+#define PCI_VENDOR_ID_ITE 0x1283
+
+const struct dev_entry devs_it8212[] = {
+ {PCI_VENDOR_ID_ITE, 0x8212, NT, "ITE", "8212F PATA RAID"},
+
+ {},
+};
+
+#define IT8212_MEMMAP_SIZE (128 * 1024)
+#define IT8212_MEMMAP_MASK (IT8212_MEMMAP_SIZE - 1)
+
+static void it8212_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static uint8_t it8212_chip_readb(const struct flashctx *flash, const chipaddr addr);
+static const struct par_master par_master_it8212 = {
+ .chip_readb = it8212_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = it8212_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int it8212_init(void)
+{
+ if (rget_io_perms())
+ return 1;
+
+ struct pci_dev *dev = pcidev_init(devs_it8212, PCI_ROM_ADDRESS);
+ if (!dev)
+ return 1;
+
+ /* Bit 0 is address decode enable, 17-31 the base address, everything else reserved/zero. */
+ uint32_t io_base_addr = pcidev_readbar(dev, PCI_ROM_ADDRESS) & 0xFFFFFFFE;
+ if (!io_base_addr)
+ return 1;
+
+ it8212_bar = rphysmap("IT8212F flash", io_base_addr, IT8212_MEMMAP_SIZE);
+ if (it8212_bar == ERROR_PTR)
+ return 1;
+
+ /* Restore ROM BAR decode state automatically at shutdown. */
+ rpci_write_long(dev, PCI_ROM_ADDRESS, io_base_addr | 0x01);
+
+ max_rom_decode.parallel = IT8212_MEMMAP_SIZE;
+ register_par_master(&par_master_it8212, BUS_PARALLEL);
+ return 0;
+}
+
+static void it8212_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ pci_mmio_writeb(val, it8212_bar + (addr & IT8212_MEMMAP_MASK));
+}
+
+static uint8_t it8212_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ return pci_mmio_readb(it8212_bar + (addr & IT8212_MEMMAP_MASK));
+}
diff --git a/it85spi.c b/it85spi.c
new file mode 100644
index 0000000..1cc8730
--- /dev/null
+++ b/it85spi.c
@@ -0,0 +1,377 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008, 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 Ronald Hoogenboom <ronald@zonnet.nl>
+ * Copyright (C) 2008 coresystems GmbH
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the ITE IT85* SPI specific routines
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "spi.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define MAX_TIMEOUT 100000
+#define MAX_TRY 5
+
+/* Constants for I/O ports */
+#define ITE_SUPERIO_PORT1 0x2e
+#define ITE_SUPERIO_PORT2 0x4e
+
+/* Legacy I/O */
+#define LEGACY_KBC_PORT_DATA 0x60
+#define LEGACY_KBC_PORT_CMD 0x64
+
+/* Constants for Logical Device registers */
+#define LDNSEL 0x07
+
+/* These are standard Super I/O 16-bit base address registers */
+#define SHM_IO_BAR0 0x60 /* big-endian, this is high bits */
+#define SHM_IO_BAR1 0x61
+
+/* The 8042 keyboard controller uses an input buffer and an output buffer to
+ * communicate with the host CPU. Both buffers are 1-byte depth. That means
+ * IBF is set to 1 when the host CPU sends a command to the input buffer
+ * of the EC. IBF is cleared to 0 once the command is read by the EC.
+ */
+#define KB_IBF (1 << 1) /* Input Buffer Full */
+#define KB_OBF (1 << 0) /* Output Buffer Full */
+
+/* IT8502 supports two access modes:
+ * LPC_MEMORY: through the memory window in 0xFFFFFxxx (follow mode)
+ * LPC_IO: through I/O port (so called indirect memory)
+ */
+#undef LPC_MEMORY
+#define LPC_IO
+
+#ifdef LPC_IO
+/* macro to fill in indirect-access registers. */
+#define INDIRECT_A0(base, value) OUTB(value, (base) + 0) /* little-endian */
+#define INDIRECT_A1(base, value) OUTB(value, (base) + 1)
+#define INDIRECT_A2(base, value) OUTB(value, (base) + 2)
+#define INDIRECT_A3(base, value) OUTB(value, (base) + 3)
+#define INDIRECT_READ(base) INB((base) + 4)
+#define INDIRECT_WRITE(base, value) OUTB(value, (base) + 4)
+#endif /* LPC_IO */
+
+#ifdef LPC_IO
+unsigned int shm_io_base;
+#endif
+unsigned char *ce_high, *ce_low;
+static int it85xx_scratch_rom_reenter = 0;
+
+/* This function will poll the keyboard status register until either
+ * an expected value shows up, or the timeout is reached.
+ * timeout is in usec.
+ *
+ * Returns: 0 -- the expected value showed up.
+ * 1 -- timeout.
+ */
+static int wait_for(const unsigned int mask, const unsigned int expected_value,
+ const int timeout, const char * error_message,
+ const char * function_name, const int lineno)
+{
+ int time_passed;
+
+ for (time_passed = 0;; ++time_passed) {
+ if ((INB(LEGACY_KBC_PORT_CMD) & mask) == expected_value)
+ return 0;
+ if (time_passed >= timeout)
+ break;
+ programmer_delay(1);
+ }
+ if (error_message)
+ msg_perr("%s():%d %s", function_name, lineno, error_message);
+ return 1;
+}
+
+/* IT8502 employs a scratch RAM when flash is being updated. Call the following
+ * two functions before/after flash erase/program. */
+void it85xx_enter_scratch_rom(void)
+{
+ int ret, tries;
+
+ msg_pdbg("%s():%d was called ...\n", __func__, __LINE__);
+ if (it85xx_scratch_rom_reenter > 0)
+ return;
+
+#if 0
+ /* FIXME: this a workaround for the bug that SMBus signal would
+ * interfere the EC firmware update. Should be removed if
+ * we find out the root cause. */
+ ret = system("stop powerd >&2");
+ if (ret)
+ msg_perr("Cannot stop powerd.\n");
+#endif
+
+ for (tries = 0; tries < MAX_TRY; ++tries) {
+ /* Wait until IBF (input buffer) is not full. */
+ if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+ "* timeout at waiting for IBF==0.\n",
+ __func__, __LINE__))
+ continue;
+
+ /* Copy EC firmware to SRAM. */
+ OUTB(0xb4, LEGACY_KBC_PORT_CMD);
+
+ /* Confirm EC has taken away the command. */
+ if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+ "* timeout at taking command.\n",
+ __func__, __LINE__))
+ continue;
+
+ /* Waiting for OBF (output buffer) has data.
+ * Note sometimes the replied command might be stolen by kernel
+ * ISR so that it is okay as long as the command is 0xFA. */
+ if (wait_for(KB_OBF, KB_OBF, MAX_TIMEOUT, NULL, NULL, 0))
+ msg_pdbg("%s():%d * timeout at waiting for OBF.\n",
+ __func__, __LINE__);
+ if ((ret = INB(LEGACY_KBC_PORT_DATA)) == 0xFA) {
+ break;
+ } else {
+ msg_perr("%s():%d * not run on SRAM ret=%d\n",
+ __func__, __LINE__, ret);
+ continue;
+ }
+ }
+
+ if (tries < MAX_TRY) {
+ /* EC already runs on SRAM */
+ it85xx_scratch_rom_reenter++;
+ msg_pdbg("%s():%d * SUCCESS.\n", __func__, __LINE__);
+ } else {
+ msg_perr("%s():%d * Max try reached.\n", __func__, __LINE__);
+ }
+}
+
+void it85xx_exit_scratch_rom(void)
+{
+#if 0
+ int ret;
+#endif
+ int tries;
+
+ msg_pdbg("%s():%d was called ...\n", __func__, __LINE__);
+ if (it85xx_scratch_rom_reenter <= 0)
+ return;
+
+ for (tries = 0; tries < MAX_TRY; ++tries) {
+ /* Wait until IBF (input buffer) is not full. */
+ if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+ "* timeout at waiting for IBF==0.\n",
+ __func__, __LINE__))
+ continue;
+
+ /* Exit SRAM. Run on flash. */
+ OUTB(0xFE, LEGACY_KBC_PORT_CMD);
+
+ /* Confirm EC has taken away the command. */
+ if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+ "* timeout at taking command.\n",
+ __func__, __LINE__)) {
+ /* We cannot ensure if EC has exited update mode.
+ * If EC is in normal mode already, a further 0xFE
+ * command will reboot system. So, exit loop here. */
+ tries = MAX_TRY;
+ break;
+ }
+
+ break;
+ }
+
+ if (tries < MAX_TRY) {
+ it85xx_scratch_rom_reenter = 0;
+ msg_pdbg("%s():%d * SUCCESS.\n", __func__, __LINE__);
+ } else {
+ msg_perr("%s():%d * Max try reached.\n", __func__, __LINE__);
+ }
+
+#if 0
+ /* FIXME: this a workaround for the bug that SMBus signal would
+ * interfere the EC firmware update. Should be removed if
+ * we find out the root cause. */
+ ret = system("start powerd >&2");
+ if (ret)
+ msg_perr("Cannot start powerd again.\n");
+#endif
+}
+
+static int it85xx_shutdown(void *data)
+{
+ msg_pdbg("%s():%d\n", __func__, __LINE__);
+ it85xx_exit_scratch_rom();
+
+ return 0; /* FIXME: Should probably return something meaningful */
+}
+
+static int it85xx_spi_common_init(struct superio s)
+{
+ chipaddr base;
+
+ msg_pdbg("%s():%d superio.vendor=0x%02x\n", __func__, __LINE__,
+ s.vendor);
+
+ if (register_shutdown(it85xx_shutdown, NULL))
+ return 1;
+
+#ifdef LPC_IO
+ /* Get LPCPNP of SHM. That's big-endian. */
+ sio_write(s.port, LDNSEL, 0x0F); /* Set LDN to SHM (0x0F) */
+ shm_io_base = (sio_read(s.port, SHM_IO_BAR0) << 8) +
+ sio_read(s.port, SHM_IO_BAR1);
+ msg_pdbg("%s():%d shm_io_base=0x%04x\n", __func__, __LINE__,
+ shm_io_base);
+
+ /* These pointers are not used directly. They will be send to EC's
+ * register for indirect access. */
+ base = 0xFFFFF000;
+ ce_high = ((unsigned char *)base) + 0xE00; /* 0xFFFFFE00 */
+ ce_low = ((unsigned char *)base) + 0xD00; /* 0xFFFFFD00 */
+
+ /* pre-set indirect-access registers since in most of cases they are
+ * 0xFFFFxx00. */
+ INDIRECT_A0(shm_io_base, base & 0xFF);
+ INDIRECT_A2(shm_io_base, (base >> 16) & 0xFF);
+ INDIRECT_A3(shm_io_base, (base >> 24));
+#endif
+#ifdef LPC_MEMORY
+ /* FIXME: We should block accessing that region for anything else.
+ * Major TODO here, and it will be a lot of work.
+ */
+ base = (chipaddr)physmap("it85 communication", 0xFFFFF000, 0x1000);
+ if (base == (chipaddr)ERROR_PTR)
+ return 1;
+
+ msg_pdbg("%s():%d base=0x%08x\n", __func__, __LINE__,
+ (unsigned int)base);
+ ce_high = (unsigned char *)(base + 0xE00); /* 0xFFFFFE00 */
+ ce_low = (unsigned char *)(base + 0xD00); /* 0xFFFFFD00 */
+#endif
+
+ return 0;
+}
+
+static int it85xx_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+
+static const struct spi_master spi_master_it85xx = {
+ .type = SPI_CONTROLLER_IT85XX,
+ .max_data_read = 64,
+ .max_data_write = 64,
+ .command = it85xx_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+int it85xx_spi_init(struct superio s)
+{
+ int ret;
+
+ if (!(internal_buses_supported & BUS_FWH)) {
+ msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
+ return 1;
+ }
+ ret = it85xx_spi_common_init(s);
+ msg_pdbg("FWH: %s():%d ret=%d\n", __func__, __LINE__, ret);
+ if (!ret) {
+ msg_pdbg("%s: internal_buses_supported=0x%x\n", __func__,
+ internal_buses_supported);
+ /* Check for FWH because IT85 listens to FWH cycles.
+ * FIXME: The big question is whether FWH cycles are necessary
+ * for communication even if LPC_IO is defined.
+ */
+ if (internal_buses_supported & BUS_FWH)
+ msg_pdbg("Registering IT85 SPI.\n");
+ /* FIXME: Really leave FWH enabled? We can't use this region
+ * anymore since accessing it would mess up IT85 communication.
+ * If we decide to disable FWH for this region, we should print
+ * a debug message about it.
+ */
+ /* Set this as SPI controller. */
+ register_spi_master(&spi_master_it85xx);
+ }
+ return ret;
+}
+
+/* According to ITE 8502 document, the procedure to follow mode is following:
+ * 1. write 0x00 to LPC/FWH address 0xffff_fexxh (drive CE# high)
+ * 2. write data to LPC/FWH address 0xffff_fdxxh (drive CE# low and MOSI
+ * with data)
+ * 3. read date from LPC/FWH address 0xffff_fdxxh (drive CE# low and get
+ * data from MISO)
+ */
+static int it85xx_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+
+ it85xx_enter_scratch_rom();
+ /* Exit scratch ROM ONLY when programmer shuts down. Otherwise, the
+ * temporary flash state may halt the EC.
+ */
+
+#ifdef LPC_IO
+ INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff);
+ INDIRECT_WRITE(shm_io_base, 0xFF); /* Write anything to this address.*/
+ INDIRECT_A1(shm_io_base, (((unsigned long int)ce_low) >> 8) & 0xff);
+#endif
+#ifdef LPC_MEMORY
+ mmio_writeb(0, ce_high);
+#endif
+ for (i = 0; i < writecnt; ++i) {
+#ifdef LPC_IO
+ INDIRECT_WRITE(shm_io_base, writearr[i]);
+#endif
+#ifdef LPC_MEMORY
+ mmio_writeb(writearr[i], ce_low);
+#endif
+ }
+ for (i = 0; i < readcnt; ++i) {
+#ifdef LPC_IO
+ readarr[i] = INDIRECT_READ(shm_io_base);
+#endif
+#ifdef LPC_MEMORY
+ readarr[i] = mmio_readb(ce_low);
+#endif
+ }
+#ifdef LPC_IO
+ INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff);
+ INDIRECT_WRITE(shm_io_base, 0xFF); /* Write anything to this address.*/
+#endif
+#ifdef LPC_MEMORY
+ mmio_writeb(0, ce_high);
+#endif
+
+ return 0;
+}
+
+#endif
diff --git a/it87spi.c b/it87spi.c
new file mode 100644
index 0000000..0a1e894
--- /dev/null
+++ b/it87spi.c
@@ -0,0 +1,438 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008, 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 Ronald Hoogenboom <ronald@zonnet.nl>
+ * Copyright (C) 2008 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the ITE IT87* SPI specific routines
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+
+#define ITE_SUPERIO_PORT1 0x2e
+#define ITE_SUPERIO_PORT2 0x4e
+
+static uint16_t it8716f_flashport = 0;
+/* use fast 33MHz SPI (<>0) or slow 16MHz (0) */
+static int fast_spi = 1;
+
+/* Helper functions for most recent ITE IT87xx Super I/O chips */
+#define CHIP_ID_BYTE1_REG 0x20
+#define CHIP_ID_BYTE2_REG 0x21
+#define CHIP_VER_REG 0x22
+void enter_conf_mode_ite(uint16_t port)
+{
+ OUTB(0x87, port);
+ OUTB(0x01, port);
+ OUTB(0x55, port);
+ if (port == ITE_SUPERIO_PORT1)
+ OUTB(0x55, port);
+ else
+ OUTB(0xaa, port);
+}
+
+void exit_conf_mode_ite(uint16_t port)
+{
+ sio_write(port, 0x02, 0x02);
+}
+
+uint16_t probe_id_ite(uint16_t port)
+{
+ uint16_t id;
+
+ enter_conf_mode_ite(port);
+ id = sio_read(port, CHIP_ID_BYTE1_REG) << 8;
+ id |= sio_read(port, CHIP_ID_BYTE2_REG);
+ exit_conf_mode_ite(port);
+
+ return id;
+}
+
+void probe_superio_ite(void)
+{
+ struct superio s = {0};
+ uint16_t ite_ports[] = {ITE_SUPERIO_PORT1, ITE_SUPERIO_PORT2, 0};
+ uint16_t *i = ite_ports;
+
+ s.vendor = SUPERIO_VENDOR_ITE;
+ for (; *i; i++) {
+ s.port = *i;
+ s.model = probe_id_ite(s.port);
+ switch (s.model >> 8) {
+ case 0x82:
+ case 0x86:
+ case 0x87:
+ /* FIXME: Print revision for all models? */
+ msg_pdbg("Found ITE Super I/O, ID 0x%04hx on port 0x%x\n", s.model, s.port);
+ register_superio(s);
+ break;
+ case 0x85:
+ msg_pdbg("Found ITE EC, ID 0x%04hx, Rev 0x%02x on port 0x%x.\n",
+ s.model, sio_read(s.port, CHIP_VER_REG), s.port);
+ register_superio(s);
+ break;
+ }
+ }
+
+ return;
+}
+
+static int it8716f_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+static int it8716f_spi_chip_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len);
+static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len);
+
+static const struct spi_master spi_master_it87xx = {
+ .type = SPI_CONTROLLER_IT87XX,
+ .max_data_read = MAX_DATA_UNSPECIFIED,
+ .max_data_write = MAX_DATA_UNSPECIFIED,
+ .command = it8716f_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = it8716f_spi_chip_read,
+ .write_256 = it8716f_spi_chip_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static uint16_t it87spi_probe(uint16_t port)
+{
+ uint8_t tmp = 0;
+ uint16_t flashport = 0;
+
+ enter_conf_mode_ite(port);
+
+ char *param = extract_programmer_param("dualbiosindex");
+ if (param != NULL) {
+ sio_write(port, 0x07, 0x07); /* Select GPIO LDN */
+ tmp = sio_read(port, 0xEF);
+ if (*param == '\0') { /* Print current setting only. */
+ free(param);
+ } else {
+ char *dualbiosindex_suffix;
+ errno = 0;
+ long chip_index = strtol(param, &dualbiosindex_suffix, 0);
+ free(param);
+ if (errno != 0 || *dualbiosindex_suffix != '\0' || chip_index < 0 || chip_index > 1) {
+ msg_perr("DualBIOS: Invalid chip index requested - choose 0 or 1.\n");
+ exit_conf_mode_ite(port);
+ return 1;
+ }
+ if (chip_index != (tmp & 1)) {
+ msg_pdbg("DualBIOS: Previous chip index: %d\n", tmp & 1);
+ sio_write(port, 0xEF, (tmp & 0xFE) | chip_index);
+ tmp = sio_read(port, 0xEF);
+ if ((tmp & 1) != chip_index) {
+ msg_perr("DualBIOS: Chip selection failed.\n");
+ exit_conf_mode_ite(port);
+ return 1;
+ }
+ }
+ }
+ msg_pinfo("DualBIOS: Selected chip: %d\n", tmp & 1);
+ }
+
+ /* NOLDN, reg 0x24, mask out lowest bit (suspend) */
+ tmp = sio_read(port, 0x24) & 0xFE;
+ /* Check if LPC->SPI translation is active. */
+ if (!(tmp & 0x0e)) {
+ msg_pdbg("No IT87* serial flash segment enabled.\n");
+ exit_conf_mode_ite(port);
+ /* Nothing to do. */
+ return 0;
+ }
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFFE0000, 0xFFFFFFFF, (tmp & 1 << 1) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0x000E0000, 0x000FFFFF, (tmp & 1 << 1) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFEE0000, 0xFFEFFFFF, (tmp & 1 << 2) ? "en" : "dis");
+ msg_pdbg("Serial flash segment 0x%08x-0x%08x %sabled\n",
+ 0xFFF80000, 0xFFFEFFFF, (tmp & 1 << 3) ? "en" : "dis");
+ msg_pdbg("LPC write to serial flash %sabled\n",
+ (tmp & 1 << 4) ? "en" : "dis");
+ /* The LPC->SPI force write enable below only makes sense for
+ * non-programmer mode.
+ */
+ /* If any serial flash segment is enabled, enable writing. */
+ if ((tmp & 0xe) && (!(tmp & 1 << 4))) {
+ msg_pdbg("Enabling LPC write to serial flash\n");
+ tmp |= 1 << 4;
+ sio_write(port, 0x24, tmp);
+ }
+ msg_pdbg("Serial flash pin %i\n", (tmp & 1 << 5) ? 87 : 29);
+ /* LDN 0x7, reg 0x64/0x65 */
+ sio_write(port, 0x07, 0x7);
+ flashport = sio_read(port, 0x64) << 8;
+ flashport |= sio_read(port, 0x65);
+ msg_pdbg("Serial flash port 0x%04x\n", flashport);
+ /* Non-default port requested? */
+ param = extract_programmer_param("it87spiport");
+ if (param) {
+ char *endptr = NULL;
+ unsigned long forced_flashport;
+ forced_flashport = strtoul(param, &endptr, 0);
+ /* Port 0, port >0x1000, unaligned ports and garbage strings
+ * are rejected.
+ */
+ if (!forced_flashport || (forced_flashport >= 0x1000) ||
+ (forced_flashport & 0x7) || (*endptr != '\0')) {
+ /* Using ports below 0x100 is a really bad idea, and
+ * should only be done if no port between 0x100 and
+ * 0xff8 works due to routing issues.
+ */
+ msg_perr("Error: it87spiport specified, but no valid "
+ "port specified.\nPort must be a multiple of "
+ "0x8 and lie between 0x100 and 0xff8.\n");
+ exit_conf_mode_ite(port);
+ free(param);
+ return 1;
+ } else {
+ flashport = (uint16_t)forced_flashport;
+ msg_pinfo("Forcing serial flash port 0x%04x\n",
+ flashport);
+ sio_write(port, 0x64, (flashport >> 8));
+ sio_write(port, 0x65, (flashport & 0xff));
+ }
+ }
+ free(param);
+ exit_conf_mode_ite(port);
+ it8716f_flashport = flashport;
+ if (internal_buses_supported & BUS_SPI)
+ msg_pdbg("Overriding chipset SPI with IT87 SPI.\n");
+ /* FIXME: Add the SPI bus or replace the other buses with it? */
+ register_spi_master(&spi_master_it87xx);
+ return 0;
+}
+
+int init_superio_ite(void)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < superio_count; i++) {
+ if (superios[i].vendor != SUPERIO_VENDOR_ITE)
+ continue;
+
+ switch (superios[i].model) {
+ case 0x8500:
+ case 0x8502:
+ case 0x8510:
+ case 0x8511:
+ case 0x8512:
+ /* FIXME: This should be enabled, but we need a check
+ * for laptop whitelisting due to the amount of things
+ * which can go wrong if the EC firmware does not
+ * implement the interface we want.
+ */
+ //it85xx_spi_init(superios[i]);
+ break;
+ case 0x8705:
+ ret |= it8705f_write_enable(superios[i].port);
+ break;
+ case 0x8716:
+ case 0x8718:
+ case 0x8720:
+ case 0x8728:
+ ret |= it87spi_probe(superios[i].port);
+ break;
+ default:
+ msg_pdbg2("Super I/O ID 0x%04hx is not on the list of flash-capable controllers.\n",
+ superios[i].model);
+ }
+ }
+ return ret;
+}
+
+/*
+ * The IT8716F only supports commands with length 1,2,4,5 bytes including
+ * command byte and can not read more than 3 bytes from the device.
+ *
+ * This function expects writearr[0] to be the first byte sent to the device,
+ * whereas the IT8716F splits commands internally into address and non-address
+ * commands with the address in inverse wire order. That's why the register
+ * ordering in case 4 and 5 may seem strange.
+ */
+static int it8716f_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ uint8_t busy, writeenc;
+ int i;
+
+ do {
+ busy = INB(it8716f_flashport) & 0x80;
+ } while (busy);
+ if (readcnt > 3) {
+ msg_pinfo("%s called with unsupported readcnt %i.\n",
+ __func__, readcnt);
+ return SPI_INVALID_LENGTH;
+ }
+ switch (writecnt) {
+ case 1:
+ OUTB(writearr[0], it8716f_flashport + 1);
+ writeenc = 0x0;
+ break;
+ case 2:
+ OUTB(writearr[0], it8716f_flashport + 1);
+ OUTB(writearr[1], it8716f_flashport + 7);
+ writeenc = 0x1;
+ break;
+ case 4:
+ OUTB(writearr[0], it8716f_flashport + 1);
+ OUTB(writearr[1], it8716f_flashport + 4);
+ OUTB(writearr[2], it8716f_flashport + 3);
+ OUTB(writearr[3], it8716f_flashport + 2);
+ writeenc = 0x2;
+ break;
+ case 5:
+ OUTB(writearr[0], it8716f_flashport + 1);
+ OUTB(writearr[1], it8716f_flashport + 4);
+ OUTB(writearr[2], it8716f_flashport + 3);
+ OUTB(writearr[3], it8716f_flashport + 2);
+ OUTB(writearr[4], it8716f_flashport + 7);
+ writeenc = 0x3;
+ break;
+ default:
+ msg_pinfo("%s called with unsupported writecnt %i.\n",
+ __func__, writecnt);
+ return SPI_INVALID_LENGTH;
+ }
+ /*
+ * Start IO, 33 or 16 MHz, readcnt input bytes, writecnt output bytes.
+ * Note:
+ * We can't use writecnt directly, but have to use a strange encoding.
+ */
+ OUTB(((0x4 + (fast_spi ? 1 : 0)) << 4)
+ | ((readcnt & 0x3) << 2) | (writeenc), it8716f_flashport);
+
+ if (readcnt > 0) {
+ do {
+ busy = INB(it8716f_flashport) & 0x80;
+ } while (busy);
+
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = INB(it8716f_flashport + 5 + i);
+ }
+
+ return 0;
+}
+
+/* Page size is usually 256 bytes */
+static int it8716f_spi_page_program(struct flashctx *flash, const uint8_t *buf, unsigned int start)
+{
+ unsigned int i;
+ int result;
+ chipaddr bios = flash->virtual_memory;
+
+ result = spi_write_enable(flash);
+ if (result)
+ return result;
+ /* FIXME: The command below seems to be redundant or wrong. */
+ OUTB(0x06, it8716f_flashport + 1);
+ OUTB(((2 + (fast_spi ? 1 : 0)) << 4), it8716f_flashport);
+ for (i = 0; i < flash->chip->page_size; i++)
+ mmio_writeb(buf[i], (void *)(bios + start + i));
+ OUTB(0, it8716f_flashport);
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 1-10 ms, so wait in 1 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1000);
+ return 0;
+}
+
+/*
+ * IT8716F only allows maximum of 512 kb SPI mapped to LPC memory cycles
+ * Need to read this big flash using firmware cycles 3 byte at a time.
+ */
+static int it8716f_spi_chip_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ fast_spi = 0;
+
+ /* FIXME: Check if someone explicitly requested to use IT87 SPI although
+ * the mainboard does not use IT87 SPI translation. This should be done
+ * via a programmer parameter for the internal programmer.
+ */
+ if ((flash->chip->total_size * 1024 > 512 * 1024)) {
+ spi_read_chunked(flash, buf, start, len, 3);
+ } else {
+ mmio_readn((void *)(flash->virtual_memory + start), buf, len);
+ }
+
+ return 0;
+}
+
+static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ const struct flashchip *chip = flash->chip;
+ /*
+ * IT8716F only allows maximum of 512 kb SPI chip size for memory
+ * mapped access. It also can't write more than 1+3+256 bytes at once,
+ * so page_size > 256 bytes needs a fallback.
+ * FIXME: Split too big page writes into chunks IT87* can handle instead
+ * of degrading to single-byte program.
+ * FIXME: Check if someone explicitly requested to use IT87 SPI although
+ * the mainboard does not use IT87 SPI translation. This should be done
+ * via a programmer parameter for the internal programmer.
+ */
+ if ((chip->total_size * 1024 > 512 * 1024) || (chip->page_size > 256)) {
+ spi_chip_write_1(flash, buf, start, len);
+ } else {
+ unsigned int lenhere;
+
+ if (start % chip->page_size) {
+ /* start to the end of the page or to start + len,
+ * whichever is smaller.
+ */
+ lenhere = min(len, chip->page_size - start % chip->page_size);
+ spi_chip_write_1(flash, buf, start, lenhere);
+ start += lenhere;
+ len -= lenhere;
+ buf += lenhere;
+ }
+
+ while (len >= chip->page_size) {
+ it8716f_spi_page_program(flash, buf, start);
+ start += chip->page_size;
+ len -= chip->page_size;
+ buf += chip->page_size;
+ }
+ if (len)
+ spi_chip_write_1(flash, buf, start, len);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/jedec.c b/jedec.c
new file mode 100644
index 0000000..af13876
--- /dev/null
+++ b/jedec.c
@@ -0,0 +1,758 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2006 Giampiero Giancipoli <gianci@email.it>
+ * Copyright (C) 2006 coresystems GmbH <info@coresystems.de>
+ * Copyright (C) 2007-2012 Carl-Daniel Hailfinger
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ * Copyright (C) 2014 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+#define MAX_REFLASH_TRIES 0x10
+#define MASK_FULL 0xffff
+#define MASK_2AA 0x7ff
+#define MASK_AAA 0xfff
+
+/* Check one byte for odd parity */
+uint8_t oddparity(uint8_t val)
+{
+ val = (val ^ (val >> 4)) & 0xf;
+ val = (val ^ (val >> 2)) & 0x3;
+ return (val ^ (val >> 1)) & 0x1;
+}
+
+static void toggle_ready_jedec_common(const struct flashctx *flash, chipaddr dst, unsigned int delay)
+{
+ unsigned int i = 0;
+ uint8_t tmp1, tmp2;
+
+ tmp1 = chip_readb(flash, dst) & 0x40;
+
+ while (i++ < 0xFFFFFFF) {
+ if (delay)
+ programmer_delay(delay);
+ tmp2 = chip_readb(flash, dst) & 0x40;
+ if (tmp1 == tmp2) {
+ break;
+ }
+ tmp1 = tmp2;
+ }
+ if (i > 0x100000)
+ msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
+}
+
+void toggle_ready_jedec(const struct flashctx *flash, chipaddr dst)
+{
+ toggle_ready_jedec_common(flash, dst, 0);
+}
+
+/* Some chips require a minimum delay between toggle bit reads.
+ * The Winbond W39V040C wants 50 ms between reads on sector erase toggle,
+ * but experiments show that 2 ms are already enough. Pick a safety factor
+ * of 4 and use an 8 ms delay.
+ * Given that erase is slow on all chips, it is recommended to use
+ * toggle_ready_jedec_slow in erase functions.
+ */
+static void toggle_ready_jedec_slow(const struct flashctx *flash, chipaddr dst)
+{
+ toggle_ready_jedec_common(flash, dst, 8 * 1000);
+}
+
+void data_polling_jedec(const struct flashctx *flash, chipaddr dst,
+ uint8_t data)
+{
+ unsigned int i = 0;
+ uint8_t tmp;
+
+ data &= 0x80;
+
+ while (i++ < 0xFFFFFFF) {
+ tmp = chip_readb(flash, dst) & 0x80;
+ if (tmp == data) {
+ break;
+ }
+ }
+ if (i > 0x100000)
+ msg_cdbg("%s: excessive loops, i=0x%x\n", __func__, i);
+}
+
+static unsigned int getaddrmask(const struct flashchip *chip)
+{
+ switch (chip->feature_bits & FEATURE_ADDR_MASK) {
+ case FEATURE_ADDR_FULL:
+ return MASK_FULL;
+ break;
+ case FEATURE_ADDR_2AA:
+ return MASK_2AA;
+ break;
+ case FEATURE_ADDR_AAA:
+ return MASK_AAA;
+ break;
+ default:
+ msg_cerr("%s called with unknown mask\n", __func__);
+ return 0;
+ break;
+ }
+}
+
+static void start_program_jedec_common(const struct flashctx *flash, unsigned int mask)
+{
+ chipaddr bios = flash->virtual_memory;
+ bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ chip_writeb(flash, 0xA0, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+}
+
+int probe_jedec_29gl(struct flashctx *flash)
+{
+ unsigned int mask = getaddrmask(flash->chip);
+ chipaddr bios = flash->virtual_memory;
+ const struct flashchip *chip = flash->chip;
+
+ /* Reset chip to a clean slate */
+ chip_writeb(flash, 0xF0, bios + (0x5555 & mask));
+
+ /* Issue JEDEC Product ID Entry command */
+ chip_writeb(flash, 0xAA, bios + (0x5555 & mask));
+ chip_writeb(flash, 0x55, bios + (0x2AAA & mask));
+ chip_writeb(flash, 0x90, bios + (0x5555 & mask));
+
+ /* Read product ID */
+ // FIXME: Continuation loop, second byte is at word 0x100/byte 0x200
+ uint32_t man_id = chip_readb(flash, bios + 0x00);
+ uint32_t dev_id = (chip_readb(flash, bios + 0x01) << 16) |
+ (chip_readb(flash, bios + 0x0E) << 8) |
+ (chip_readb(flash, bios + 0x0F) << 0);
+
+ /* Issue JEDEC Product ID Exit command */
+ chip_writeb(flash, 0xF0, bios + (0x5555 & mask));
+
+ msg_cdbg("%s: man_id 0x%02x, dev_id 0x%06x", __func__, man_id, dev_id);
+ if (!oddparity(man_id))
+ msg_cdbg(", man_id parity violation");
+
+ /* Read the product ID location again. We should now see normal flash contents. */
+ uint32_t flashcontent1 = chip_readb(flash, bios + 0x00); // FIXME: Continuation loop
+ uint32_t flashcontent2 = (chip_readb(flash, bios + 0x01) << 16) |
+ (chip_readb(flash, bios + 0x0E) << 8) |
+ (chip_readb(flash, bios + 0x0F) << 0);
+
+ if (man_id == flashcontent1)
+ msg_cdbg(", man_id seems to be normal flash content");
+ if (dev_id == flashcontent2)
+ msg_cdbg(", dev_id seems to be normal flash content");
+
+ msg_cdbg("\n");
+ if (man_id != chip->manufacture_id || dev_id != chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+static int probe_jedec_common(struct flashctx *flash, unsigned int mask)
+{
+ chipaddr bios = flash->virtual_memory;
+ const struct flashchip *chip = flash->chip;
+ bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+ uint8_t id1, id2;
+ uint32_t largeid1, largeid2;
+ uint32_t flashcontent1, flashcontent2;
+ unsigned int probe_timing_enter, probe_timing_exit;
+
+ if (chip->probe_timing > 0)
+ probe_timing_enter = probe_timing_exit = chip->probe_timing;
+ else if (chip->probe_timing == TIMING_ZERO) { /* No delay. */
+ probe_timing_enter = probe_timing_exit = 0;
+ } else if (chip->probe_timing == TIMING_FIXME) { /* == _IGNORED */
+ msg_cdbg("Chip lacks correct probe timing information, using default 10ms/40us. ");
+ probe_timing_enter = 10000;
+ probe_timing_exit = 40;
+ } else {
+ msg_cerr("Chip has negative value in probe_timing, failing without chip access\n");
+ return 0;
+ }
+
+ /* Earlier probes might have been too fast for the chip to enter ID
+ * mode completely. Allow the chip to finish this before seeing a
+ * reset command.
+ */
+ if (probe_timing_enter)
+ programmer_delay(probe_timing_enter);
+ /* Reset chip to a clean slate */
+ if ((chip->feature_bits & FEATURE_RESET_MASK) == FEATURE_LONG_RESET)
+ {
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_exit)
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ if (probe_timing_exit)
+ programmer_delay(10);
+ }
+ chip_writeb(flash, 0xF0, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_exit)
+ programmer_delay(probe_timing_exit);
+
+ /* Issue JEDEC Product ID Entry command */
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_enter)
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ if (probe_timing_enter)
+ programmer_delay(10);
+ chip_writeb(flash, 0x90, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_enter)
+ programmer_delay(probe_timing_enter);
+
+ /* Read product ID */
+ id1 = chip_readb(flash, bios + (0x00 << shifted));
+ id2 = chip_readb(flash, bios + (0x01 << shifted));
+ largeid1 = id1;
+ largeid2 = id2;
+
+ /* Check if it is a continuation ID, this should be a while loop. */
+ if (id1 == 0x7F) {
+ largeid1 <<= 8;
+ id1 = chip_readb(flash, bios + 0x100);
+ largeid1 |= id1;
+ }
+ if (id2 == 0x7F) {
+ largeid2 <<= 8;
+ id2 = chip_readb(flash, bios + 0x101);
+ largeid2 |= id2;
+ }
+
+ /* Issue JEDEC Product ID Exit command */
+ if ((chip->feature_bits & FEATURE_RESET_MASK) == FEATURE_LONG_RESET)
+ {
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_exit)
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ if (probe_timing_exit)
+ programmer_delay(10);
+ }
+ chip_writeb(flash, 0xF0, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ if (probe_timing_exit)
+ programmer_delay(probe_timing_exit);
+
+ msg_cdbg("%s: id1 0x%02x, id2 0x%02x", __func__, largeid1, largeid2);
+ if (!oddparity(id1))
+ msg_cdbg(", id1 parity violation");
+
+ /* Read the product ID location again. We should now see normal flash contents. */
+ flashcontent1 = chip_readb(flash, bios + (0x00 << shifted));
+ flashcontent2 = chip_readb(flash, bios + (0x01 << shifted));
+
+ /* Check if it is a continuation ID, this should be a while loop. */
+ if (flashcontent1 == 0x7F) {
+ flashcontent1 <<= 8;
+ flashcontent1 |= chip_readb(flash, bios + 0x100);
+ }
+ if (flashcontent2 == 0x7F) {
+ flashcontent2 <<= 8;
+ flashcontent2 |= chip_readb(flash, bios + 0x101);
+ }
+
+ if (largeid1 == flashcontent1)
+ msg_cdbg(", id1 is normal flash content");
+ if (largeid2 == flashcontent2)
+ msg_cdbg(", id2 is normal flash content");
+
+ msg_cdbg("\n");
+ if (largeid1 != chip->manufacture_id || largeid2 != chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+static int erase_sector_jedec_common(struct flashctx *flash, unsigned int page,
+ unsigned int pagesize, unsigned int mask)
+{
+ chipaddr bios = flash->virtual_memory;
+ bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+ unsigned int delay_us = 0;
+
+ if(flash->chip->probe_timing != TIMING_ZERO)
+ delay_us = 10;
+
+ /* Issue the Sector Erase command */
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x80, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x30, bios + page);
+ programmer_delay(delay_us);
+
+ /* wait for Toggle bit ready */
+ toggle_ready_jedec_slow(flash, bios);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+static int erase_block_jedec_common(struct flashctx *flash, unsigned int block,
+ unsigned int blocksize, unsigned int mask)
+{
+ chipaddr bios = flash->virtual_memory;
+ bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+ unsigned int delay_us = 0;
+
+ if(flash->chip->probe_timing != TIMING_ZERO)
+ delay_us = 10;
+
+ /* Issue the Sector Erase command */
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x80, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x50, bios + block);
+ programmer_delay(delay_us);
+
+ /* wait for Toggle bit ready */
+ toggle_ready_jedec_slow(flash, bios);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+static int erase_chip_jedec_common(struct flashctx *flash, unsigned int mask)
+{
+ chipaddr bios = flash->virtual_memory;
+ bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+ unsigned int delay_us = 0;
+
+ if(flash->chip->probe_timing != TIMING_ZERO)
+ delay_us = 10;
+
+ /* Issue the JEDEC Chip Erase command */
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x80, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+
+ chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
+ programmer_delay(delay_us);
+ chip_writeb(flash, 0x10, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
+ programmer_delay(delay_us);
+
+ toggle_ready_jedec_slow(flash, bios);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+static int write_byte_program_jedec_common(const struct flashctx *flash, const uint8_t *src,
+ chipaddr dst, unsigned int mask)
+{
+ int tried = 0, failed = 0;
+ chipaddr bios = flash->virtual_memory;
+
+ /* If the data is 0xFF, don't program it and don't complain. */
+ if (*src == 0xFF) {
+ return 0;
+ }
+
+retry:
+ /* Issue JEDEC Byte Program command */
+ start_program_jedec_common(flash, mask);
+
+ /* transfer data from source to destination */
+ chip_writeb(flash, *src, dst);
+ toggle_ready_jedec(flash, bios);
+
+ if (chip_readb(flash, dst) != *src && tried++ < MAX_REFLASH_TRIES) {
+ goto retry;
+ }
+
+ if (tried >= MAX_REFLASH_TRIES)
+ failed = 1;
+
+ return failed;
+}
+
+/* chunksize is 1 */
+int write_jedec_1(struct flashctx *flash, const uint8_t *src, unsigned int start,
+ unsigned int len)
+{
+ int i, failed = 0;
+ chipaddr dst = flash->virtual_memory + start;
+ chipaddr olddst;
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+
+ olddst = dst;
+ for (i = 0; i < len; i++) {
+ if (write_byte_program_jedec_common(flash, src, dst, mask))
+ failed = 1;
+ dst++, src++;
+ }
+ if (failed)
+ msg_cerr(" writing sector at 0x%" PRIxPTR " failed!\n", olddst);
+
+ return failed;
+}
+
+static int write_page_write_jedec_common(struct flashctx *flash, const uint8_t *src,
+ unsigned int start, unsigned int page_size)
+{
+ int i, tried = 0, failed;
+ const uint8_t *s = src;
+ chipaddr bios = flash->virtual_memory;
+ chipaddr dst = bios + start;
+ chipaddr d = dst;
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+
+retry:
+ /* Issue JEDEC Start Program command */
+ start_program_jedec_common(flash, mask);
+
+ /* transfer data from source to destination */
+ for (i = 0; i < page_size; i++) {
+ /* If the data is 0xFF, don't program it */
+ if (*src != 0xFF)
+ chip_writeb(flash, *src, dst);
+ dst++;
+ src++;
+ }
+
+ toggle_ready_jedec(flash, dst - 1);
+
+ dst = d;
+ src = s;
+ failed = verify_range(flash, src, start, page_size);
+
+ if (failed && tried++ < MAX_REFLASH_TRIES) {
+ msg_cerr("retrying.\n");
+ goto retry;
+ }
+ if (failed) {
+ msg_cerr(" page 0x%" PRIxPTR " failed!\n", (d - bios) / page_size);
+ }
+ return failed;
+}
+
+/* chunksize is page_size */
+/*
+ * Write a part of the flash chip.
+ * FIXME: Use the chunk code from Michael Karcher instead.
+ * This function is a slightly modified copy of spi_write_chunked.
+ * Each page is written separately in chunks with a maximum size of chunksize.
+ */
+int write_jedec(struct flashctx *flash, const uint8_t *buf, unsigned int start,
+ int unsigned len)
+{
+ unsigned int i, starthere, lenhere;
+ /* FIXME: page_size is the wrong variable. We need max_writechunk_size
+ * in struct flashctx to do this properly. All chips using
+ * write_jedec have page_size set to max_writechunk_size, so
+ * we're OK for now.
+ */
+ unsigned int page_size = flash->chip->page_size;
+
+ /* Warning: This loop has a very unusual condition and body.
+ * The loop needs to go through each page with at least one affected
+ * byte. The lowest page number is (start / page_size) since that
+ * division rounds down. The highest page number we want is the page
+ * where the last byte of the range lives. That last byte has the
+ * address (start + len - 1), thus the highest page number is
+ * (start + len - 1) / page_size. Since we want to include that last
+ * page as well, the loop condition uses <=.
+ */
+ for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+ /* Byte position of the first byte in the range in this page. */
+ /* starthere is an offset to the base address of the chip. */
+ starthere = max(start, i * page_size);
+ /* Length of bytes in the range in this page. */
+ lenhere = min(start + len, (i + 1) * page_size) - starthere;
+
+ if (write_page_write_jedec_common(flash, buf + starthere - start, starthere, lenhere))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* erase chip with block_erase() prototype */
+int erase_chip_block_jedec(struct flashctx *flash, unsigned int addr,
+ unsigned int blocksize)
+{
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+ if ((addr != 0) || (blocksize != flash->chip->total_size * 1024)) {
+ msg_cerr("%s called with incorrect arguments\n",
+ __func__);
+ return -1;
+ }
+ return erase_chip_jedec_common(flash, mask);
+}
+
+int probe_jedec(struct flashctx *flash)
+{
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+ return probe_jedec_common(flash, mask);
+}
+
+int erase_sector_jedec(struct flashctx *flash, unsigned int page,
+ unsigned int size)
+{
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+ return erase_sector_jedec_common(flash, page, size, mask);
+}
+
+int erase_block_jedec(struct flashctx *flash, unsigned int page,
+ unsigned int size)
+{
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+ return erase_block_jedec_common(flash, page, size, mask);
+}
+
+int erase_chip_jedec(struct flashctx *flash)
+{
+ unsigned int mask;
+
+ mask = getaddrmask(flash->chip);
+ return erase_chip_jedec_common(flash, mask);
+}
+
+struct unlockblock {
+ unsigned int size;
+ unsigned int count;
+};
+
+typedef int (*unlockblock_func)(const struct flashctx *flash, chipaddr offset);
+static int regspace2_walk_unlockblocks(const struct flashctx *flash, const struct unlockblock *block, unlockblock_func func)
+{
+ chipaddr off = flash->virtual_registers + 2;
+ while (block->count != 0) {
+ unsigned int j;
+ for (j = 0; j < block->count; j++) {
+ if (func(flash, off))
+ return -1;
+ off += block->size;
+ }
+ block++;
+ }
+ return 0;
+}
+
+#define REG2_RWLOCK ((1 << 2) | (1 << 0))
+#define REG2_LOCKDOWN (1 << 1)
+#define REG2_MASK (REG2_RWLOCK | REG2_LOCKDOWN)
+
+static int printlock_regspace2_block(const struct flashctx *flash, chipaddr lockreg)
+{
+ uint8_t state = chip_readb(flash, lockreg);
+ msg_cdbg("Lock status of block at 0x%0*" PRIxPTR " is ", PRIxPTR_WIDTH, lockreg);
+ switch (state & REG2_MASK) {
+ case 0:
+ msg_cdbg("Full Access.\n");
+ break;
+ case 1:
+ msg_cdbg("Write Lock (Default State).\n");
+ break;
+ case 2:
+ msg_cdbg("Locked Open (Full Access, Locked Down).\n");
+ break;
+ case 3:
+ msg_cdbg("Write Lock, Locked Down.\n");
+ break;
+ case 4:
+ msg_cdbg("Read Lock.\n");
+ break;
+ case 5:
+ msg_cdbg("Read/Write Lock.\n");
+ break;
+ case 6:
+ msg_cdbg("Read Lock, Locked Down.\n");
+ break;
+ case 7:
+ msg_cdbg("Read/Write Lock, Locked Down.\n");
+ break;
+ }
+ return 0;
+}
+
+int printlock_regspace2_blocks(const struct flashctx *flash, const struct unlockblock *blocks)
+{
+ return regspace2_walk_unlockblocks(flash, blocks, &printlock_regspace2_block);
+}
+
+static int printlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
+{
+ const unsigned int elems = flash->chip->total_size * 1024 / block_size;
+ struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
+ return regspace2_walk_unlockblocks(flash, blocks, &printlock_regspace2_block);
+}
+
+int printlock_regspace2_uniform_64k(struct flashctx *flash)
+{
+ return printlock_regspace2_uniform(flash, 64 * 1024);
+}
+
+int printlock_regspace2_block_eraser_0(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
+}
+
+int printlock_regspace2_block_eraser_1(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
+}
+
+/* Try to change the lock register at address lockreg from cur to new.
+ *
+ * - Try to unlock the lock bit if requested and it is currently set (although this is probably futile).
+ * - Try to change the read/write bits if requested.
+ * - Try to set the lockdown bit if requested.
+ * Return an error immediately if any of this fails. */
+static int changelock_regspace2_block(const struct flashctx *flash, chipaddr lockreg, uint8_t cur, uint8_t new)
+{
+ /* Only allow changes to known read/write/lockdown bits */
+ if (((cur ^ new) & ~REG2_MASK) != 0) {
+ msg_cerr("Invalid lock change from 0x%02x to 0x%02x requested at 0x%0*" PRIxPTR "!\n"
+ "Please report a bug at flashrom@flashrom.org\n",
+ cur, new, PRIxPTR_WIDTH, lockreg);
+ return -1;
+ }
+
+ /* Exit early if no change (of read/write/lockdown bits) was requested. */
+ if (((cur ^ new) & REG2_MASK) == 0) {
+ msg_cdbg2("Lock bits at 0x%0*" PRIxPTR " not changed.\n", PRIxPTR_WIDTH, lockreg);
+ return 0;
+ }
+
+ /* Normally the lockdown bit can not be cleared. Try nevertheless if requested. */
+ if ((cur & REG2_LOCKDOWN) && !(new & REG2_LOCKDOWN)) {
+ chip_writeb(flash, cur & ~REG2_LOCKDOWN, lockreg);
+ cur = chip_readb(flash, lockreg);
+ if ((cur & REG2_LOCKDOWN) == REG2_LOCKDOWN) {
+ msg_cwarn("Lockdown can't be removed at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
+ PRIxPTR_WIDTH, lockreg, cur);
+ return -1;
+ }
+ }
+
+ /* Change read and/or write bit */
+ if ((cur ^ new) & REG2_RWLOCK) {
+ /* Do not lockdown yet. */
+ uint8_t wanted = (cur & ~REG2_RWLOCK) | (new & REG2_RWLOCK);
+ chip_writeb(flash, wanted, lockreg);
+ cur = chip_readb(flash, lockreg);
+ if (cur != wanted) {
+ msg_cerr("Changing lock bits failed at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
+ PRIxPTR_WIDTH, lockreg, cur);
+ return -1;
+ }
+ msg_cdbg("Changed lock bits at 0x%0*" PRIxPTR " to 0x%02x.\n",
+ PRIxPTR_WIDTH, lockreg, cur);
+ }
+
+ /* Eventually, enable lockdown if requested. */
+ if (!(cur & REG2_LOCKDOWN) && (new & REG2_LOCKDOWN)) {
+ chip_writeb(flash, new, lockreg);
+ cur = chip_readb(flash, lockreg);
+ if (cur != new) {
+ msg_cerr("Enabling lockdown FAILED at 0x%0*" PRIxPTR "! New value: 0x%02x.\n",
+ PRIxPTR_WIDTH, lockreg, cur);
+ return -1;
+ }
+ msg_cdbg("Enabled lockdown at 0x%0*" PRIxPTR ".\n", PRIxPTR_WIDTH, lockreg);
+ }
+
+ return 0;
+}
+
+static int unlock_regspace2_block_generic(const struct flashctx *flash, chipaddr lockreg)
+{
+ uint8_t old = chip_readb(flash, lockreg);
+ /* We don't care for the lockdown bit as long as the RW locks are 0 after we're done */
+ return changelock_regspace2_block(flash, lockreg, old, old & ~REG2_RWLOCK);
+}
+
+static int unlock_regspace2_uniform(struct flashctx *flash, unsigned long block_size)
+{
+ const unsigned int elems = flash->chip->total_size * 1024 / block_size;
+ struct unlockblock blocks[2] = {{.size = block_size, .count = elems}};
+ return regspace2_walk_unlockblocks(flash, blocks, &unlock_regspace2_block_generic);
+}
+
+int unlock_regspace2_uniform_64k(struct flashctx *flash)
+{
+ return unlock_regspace2_uniform(flash, 64 * 1024);
+}
+
+int unlock_regspace2_uniform_32k(struct flashctx *flash)
+{
+ return unlock_regspace2_uniform(flash, 32 * 1024);
+}
+
+int unlock_regspace2_block_eraser_0(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
+}
+
+int unlock_regspace2_block_eraser_1(struct flashctx *flash)
+{
+ // FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
+ const struct unlockblock *unlockblocks =
+ (const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+ return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
+}
+
diff --git a/layout.c b/layout.c
new file mode 100644
index 0000000..f71eeaa
--- /dev/null
+++ b/layout.c
@@ -0,0 +1,315 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH)
+ * Copyright (C) 2011-2013 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "flash.h"
+#include "programmer.h"
+
+#define MAX_ROMLAYOUT 32
+
+typedef struct {
+ chipoff_t start;
+ chipoff_t end;
+ unsigned int included;
+ char name[256];
+} romentry_t;
+
+/* rom_entries store the entries specified in a layout file and associated run-time data */
+static romentry_t rom_entries[MAX_ROMLAYOUT];
+static int num_rom_entries = 0; /* the number of successfully parsed rom_entries */
+
+/* include_args holds the arguments specified at the command line with -i. They must be processed at some point
+ * so that desired regions are marked as "included" in the rom_entries list. */
+static char *include_args[MAX_ROMLAYOUT];
+static int num_include_args = 0; /* the number of valid include_args. */
+
+#ifndef __LIBPAYLOAD__
+int read_romlayout(const char *name)
+{
+ FILE *romlayout;
+ char tempstr[256];
+ int i;
+
+ romlayout = fopen(name, "r");
+
+ if (!romlayout) {
+ msg_gerr("ERROR: Could not open ROM layout (%s).\n",
+ name);
+ return -1;
+ }
+
+ while (!feof(romlayout)) {
+ char *tstr1, *tstr2;
+
+ if (num_rom_entries >= MAX_ROMLAYOUT) {
+ msg_gerr("Maximum number of ROM images (%i) in layout "
+ "file reached.\n", MAX_ROMLAYOUT);
+ (void)fclose(romlayout);
+ return 1;
+ }
+ if (2 != fscanf(romlayout, "%255s %255s\n", tempstr, rom_entries[num_rom_entries].name))
+ continue;
+#if 0
+ // fscanf does not like arbitrary comments like that :( later
+ if (tempstr[0] == '#') {
+ continue;
+ }
+#endif
+ tstr1 = strtok(tempstr, ":");
+ tstr2 = strtok(NULL, ":");
+ if (!tstr1 || !tstr2) {
+ msg_gerr("Error parsing layout file. Offending string: \"%s\"\n", tempstr);
+ (void)fclose(romlayout);
+ return 1;
+ }
+ rom_entries[num_rom_entries].start = strtol(tstr1, (char **)NULL, 16);
+ rom_entries[num_rom_entries].end = strtol(tstr2, (char **)NULL, 16);
+ rom_entries[num_rom_entries].included = 0;
+ num_rom_entries++;
+ }
+
+ for (i = 0; i < num_rom_entries; i++) {
+ msg_gdbg("romlayout %08x - %08x named %s\n",
+ rom_entries[i].start,
+ rom_entries[i].end, rom_entries[i].name);
+ }
+
+ (void)fclose(romlayout);
+
+ return 0;
+}
+#endif
+
+/* returns the index of the entry (or a negative value if it is not found) */
+static int find_include_arg(const char *const name)
+{
+ unsigned int i;
+ for (i = 0; i < num_include_args; i++) {
+ if (!strcmp(include_args[i], name))
+ return i;
+ }
+ return -1;
+}
+
+/* register an include argument (-i) for later processing */
+int register_include_arg(char *name)
+{
+ if (num_include_args >= MAX_ROMLAYOUT) {
+ msg_gerr("Too many regions included (%i).\n", num_include_args);
+ return 1;
+ }
+
+ if (name == NULL) {
+ msg_gerr("<NULL> is a bad region name.\n");
+ return 1;
+ }
+
+ if (find_include_arg(name) != -1) {
+ msg_gerr("Duplicate region name: \"%s\".\n", name);
+ return 1;
+ }
+
+ include_args[num_include_args] = name;
+ num_include_args++;
+ return 0;
+}
+
+/* returns the index of the entry (or a negative value if it is not found) */
+static int find_romentry(char *name)
+{
+ int i;
+
+ if (num_rom_entries == 0)
+ return -1;
+
+ msg_gspew("Looking for region \"%s\"... ", name);
+ for (i = 0; i < num_rom_entries; i++) {
+ if (!strcmp(rom_entries[i].name, name)) {
+ rom_entries[i].included = 1;
+ msg_gspew("found.\n");
+ return i;
+ }
+ }
+ msg_gspew("not found.\n");
+ return -1;
+}
+
+/* process -i arguments
+ * returns 0 to indicate success, >0 to indicate failure
+ */
+int process_include_args(void)
+{
+ int i;
+ unsigned int found = 0;
+
+ if (num_include_args == 0)
+ return 0;
+
+ /* User has specified an area, but no layout file is loaded. */
+ if (num_rom_entries == 0) {
+ msg_gerr("Region requested (with -i \"%s\"), "
+ "but no layout data is available.\n",
+ include_args[0]);
+ return 1;
+ }
+
+ for (i = 0; i < num_include_args; i++) {
+ if (find_romentry(include_args[i]) < 0) {
+ msg_gerr("Invalid region specified: \"%s\".\n",
+ include_args[i]);
+ return 1;
+ }
+ found++;
+ }
+
+ msg_ginfo("Using region%s: \"%s\"", num_include_args > 1 ? "s" : "",
+ include_args[0]);
+ for (i = 1; i < num_include_args; i++)
+ msg_ginfo(", \"%s\"", include_args[i]);
+ msg_ginfo(".\n");
+ return 0;
+}
+
+void layout_cleanup(void)
+{
+ int i;
+ for (i = 0; i < num_include_args; i++) {
+ free(include_args[i]);
+ include_args[i] = NULL;
+ }
+ num_include_args = 0;
+
+ for (i = 0; i < num_rom_entries; i++) {
+ rom_entries[i].included = 0;
+ }
+ num_rom_entries = 0;
+}
+
+romentry_t *get_next_included_romentry(unsigned int start)
+{
+ int i;
+ unsigned int best_start = UINT_MAX;
+ romentry_t *best_entry = NULL;
+ romentry_t *cur;
+
+ /* First come, first serve for overlapping regions. */
+ for (i = 0; i < num_rom_entries; i++) {
+ cur = &rom_entries[i];
+ if (!cur->included)
+ continue;
+ /* Already past the current entry? */
+ if (start > cur->end)
+ continue;
+ /* Inside the current entry? */
+ if (start >= cur->start)
+ return cur;
+ /* Entry begins after start. */
+ if (best_start > cur->start) {
+ best_start = cur->start;
+ best_entry = cur;
+ }
+ }
+ return best_entry;
+}
+
+/* Validate and - if needed - normalize layout entries. */
+int normalize_romentries(const struct flashctx *flash)
+{
+ chipsize_t total_size = flash->chip->total_size * 1024;
+ int ret = 0;
+
+ int i;
+ for (i = 0; i < num_rom_entries; i++) {
+ if (rom_entries[i].start >= total_size || rom_entries[i].end >= total_size) {
+ msg_gwarn("Warning: Address range of region \"%s\" exceeds the current chip's "
+ "address space.\n", rom_entries[i].name);
+ if (rom_entries[i].included)
+ ret = 1;
+ }
+ if (rom_entries[i].start > rom_entries[i].end) {
+ msg_gerr("Error: Size of the address range of region \"%s\" is not positive.\n",
+ rom_entries[i].name);
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+static int copy_old_content(struct flashctx *flash, int oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents, unsigned int start, unsigned int size)
+{
+ if (!oldcontents_valid) {
+ /* oldcontents is a zero-filled buffer. By reading the current data into oldcontents here, we
+ * avoid a rewrite of identical regions even if an initial full chip read didn't happen. */
+ msg_gdbg2("Read a chunk starting at 0x%06x (len=0x%06x).\n", start, size);
+ int ret = flash->chip->read(flash, oldcontents + start, start, size);
+ if (ret != 0) {
+ msg_gerr("Failed to read chunk 0x%06x-0x%06x.\n", start, start + size - 1);
+ return 1;
+ }
+ }
+ memcpy(newcontents + start, oldcontents + start, size);
+ return 0;
+}
+
+/**
+ * Modify @newcontents so that it contains the data that should be on the chip eventually. In the case the user
+ * wants to update only parts of it, copy the chunks to be preserved from @oldcontents to @newcontents. If
+ * @oldcontents is not valid, we need to fetch the current data from the chip first.
+ */
+int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents)
+{
+ unsigned int start = 0;
+ romentry_t *entry;
+ unsigned int size = flash->chip->total_size * 1024;
+
+ /* If no regions were specified for inclusion, assume
+ * that the user wants to write the complete new image.
+ */
+ if (num_include_args == 0)
+ return 0;
+
+ /* Non-included romentries are ignored.
+ * The union of all included romentries is used from the new image.
+ */
+ while (start < size) {
+ entry = get_next_included_romentry(start);
+ /* No more romentries for remaining region? */
+ if (!entry) {
+ copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
+ size - start);
+ break;
+ }
+ /* For non-included region, copy from old content. */
+ if (entry->start > start)
+ copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
+ entry->start - start);
+ /* Skip to location after current romentry. */
+ start = entry->end + 1;
+ /* Catch overflow. */
+ if (!start)
+ break;
+ }
+ return 0;
+}
diff --git a/linux_spi.c b/linux_spi.c
new file mode 100644
index 0000000..e51fbc4
--- /dev/null
+++ b/linux_spi.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Sven Schnelle <svens@stackframe.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if CONFIG_LINUX_SPI == 1
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+#include <linux/ioctl.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* Devices known to work with this module (FIXME: export as struct dev_entry):
+ * Beagle Bone Black
+ * Raspberry Pi
+ * HummingBoard
+ */
+
+static int fd = -1;
+
+static int linux_spi_shutdown(void *data);
+static int linux_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *txbuf,
+ unsigned char *rxbuf);
+static int linux_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len);
+static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf,
+ unsigned int start, unsigned int len);
+
+static const struct spi_master spi_master_linux = {
+ .type = SPI_CONTROLLER_LINUX,
+ .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */
+ .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */
+ .command = linux_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = linux_spi_read,
+ .write_256 = linux_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+int linux_spi_init(void)
+{
+ char *p, *endp, *dev;
+ uint32_t speed_hz = 0;
+ /* FIXME: make the following configurable by CLI options. */
+ /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */
+ const uint8_t mode = SPI_MODE_0;
+ const uint8_t bits = 8;
+
+ p = extract_programmer_param("spispeed");
+ if (p && strlen(p)) {
+ speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000;
+ if (p == endp) {
+ msg_perr("%s: invalid clock: %s kHz\n", __func__, p);
+ free(p);
+ return 1;
+ }
+ }
+ free(p);
+
+ dev = extract_programmer_param("dev");
+ if (!dev || !strlen(dev)) {
+ msg_perr("No SPI device given. Use flashrom -p "
+ "linux_spi:dev=/dev/spidevX.Y\n");
+ free(dev);
+ return 1;
+ }
+
+ msg_pdbg("Using device %s\n", dev);
+ if ((fd = open(dev, O_RDWR)) == -1) {
+ msg_perr("%s: failed to open %s: %s\n", __func__,
+ dev, strerror(errno));
+ free(dev);
+ return 1;
+ }
+ free(dev);
+
+ if (register_shutdown(linux_spi_shutdown, NULL))
+ return 1;
+ /* We rely on the shutdown function for cleanup from here on. */
+
+ if (speed_hz > 0) {
+ if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) {
+ msg_perr("%s: failed to set speed to %d Hz: %s\n",
+ __func__, speed_hz, strerror(errno));
+ return 1;
+ }
+
+ msg_pdbg("Using %d kHz clock\n", speed_hz/1000);
+ }
+
+ if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
+ msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n",
+ __func__, mode, strerror(errno));
+ return 1;
+ }
+
+ if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) {
+ msg_perr("%s: failed to set the number of bits per SPI word to %u: %s\n",
+ __func__, bits == 0 ? 8 : bits, strerror(errno));
+ return 1;
+ }
+
+ register_spi_master(&spi_master_linux);
+
+ return 0;
+}
+
+static int linux_spi_shutdown(void *data)
+{
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+ return 0;
+}
+
+static int linux_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *txbuf,
+ unsigned char *rxbuf)
+{
+ int iocontrol_code;
+ struct spi_ioc_transfer msg[2] = {
+ {
+ .tx_buf = (uint64_t)(uintptr_t)txbuf,
+ .len = writecnt,
+ },
+ {
+ .rx_buf = (uint64_t)(uintptr_t)rxbuf,
+ .len = readcnt,
+ },
+ };
+
+ if (fd == -1)
+ return -1;
+ /* The implementation currently does not support requests that
+ don't start with sending a command. */
+ if (writecnt == 0)
+ return SPI_INVALID_LENGTH;
+
+ /* Just submit the first (write) request in case there is nothing
+ to read. Otherwise submit both requests. */
+ if (readcnt == 0)
+ iocontrol_code = SPI_IOC_MESSAGE(1);
+ else
+ iocontrol_code = SPI_IOC_MESSAGE(2);
+
+ if (ioctl(fd, iocontrol_code, msg) == -1) {
+ msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int linux_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ return spi_read_chunked(flash, buf, start, len,
+ (unsigned int)getpagesize());
+}
+
+static int linux_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return spi_write_chunked(flash, buf, start, len,
+ ((unsigned int)getpagesize()) - 4);
+}
+
+#endif // CONFIG_LINUX_SPI == 1
diff --git a/mcp6x_spi.c b/mcp6x_spi.c
new file mode 100644
index 0000000..4a57cb0
--- /dev/null
+++ b/mcp6x_spi.c
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
+ * Based on clean room reverse engineered docs from
+ * https://flashrom.org/pipermail/flashrom/2009-December/001180.html
+ * created by Michael Karcher.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include <ctype.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+/* Bit positions for each pin. */
+
+#define MCP6X_SPI_CS 1
+#define MCP6X_SPI_SCK 2
+#define MCP6X_SPI_MOSI 3
+#define MCP6X_SPI_MISO 4
+#define MCP6X_SPI_REQUEST 0
+#define MCP6X_SPI_GRANT 8
+
+void *mcp6x_spibar = NULL;
+
+/* Cached value of last GPIO state. */
+static uint8_t mcp_gpiostate;
+
+static void mcp6x_request_spibus(void)
+{
+ mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
+ mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
+ mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
+
+ /* Wait until we are allowed to use the SPI bus. */
+ while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
+
+ /* Update the cache. */
+ mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_release_spibus(void)
+{
+ mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
+ mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_bitbang_set_cs(int val)
+{
+ mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
+ mcp_gpiostate |= (val << MCP6X_SPI_CS);
+ mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_bitbang_set_sck(int val)
+{
+ mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
+ mcp_gpiostate |= (val << MCP6X_SPI_SCK);
+ mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_bitbang_set_mosi(int val)
+{
+ mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
+ mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
+ mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
+}
+
+static int mcp6x_bitbang_get_miso(void)
+{
+ mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
+ return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
+ .type = BITBANG_SPI_MASTER_MCP,
+ .set_cs = mcp6x_bitbang_set_cs,
+ .set_sck = mcp6x_bitbang_set_sck,
+ .set_mosi = mcp6x_bitbang_set_mosi,
+ .get_miso = mcp6x_bitbang_get_miso,
+ .request_bus = mcp6x_request_spibus,
+ .release_bus = mcp6x_release_spibus,
+ .half_period = 0,
+};
+
+int mcp6x_spi_init(int want_spi)
+{
+ uint16_t status;
+ uint32_t mcp6x_spibaraddr;
+ struct pci_dev *smbusdev;
+
+ /* Look for the SMBus device (SMBus PCI class) */
+ smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
+ if (!smbusdev) {
+ if (want_spi) {
+ msg_perr("ERROR: SMBus device not found. Not enabling "
+ "SPI.\n");
+ return 1;
+ } else {
+ msg_pinfo("Odd. SMBus device not found.\n");
+ return 0;
+ }
+ }
+ msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
+ smbusdev->vendor_id, smbusdev->device_id,
+ smbusdev->bus, smbusdev->dev, smbusdev->func);
+
+
+ /* Locate the BAR where the SPI interface lives. */
+ mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
+ /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
+ * 32-bit non-prefetchable memory BAR.
+ */
+ mcp6x_spibaraddr &= ~0xffff;
+ msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
+
+ /* Accessing a NULL pointer BAR is evil. Don't do it. */
+ if (!mcp6x_spibaraddr && want_spi) {
+ msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
+ return 1;
+ } else if (!mcp6x_spibaraddr && !want_spi) {
+ msg_pdbg("MCP SPI is not used.\n");
+ return 0;
+ } else if (mcp6x_spibaraddr && !want_spi) {
+ msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
+ /* FIXME: Should we enable SPI anyway? */
+ return 0;
+ }
+ /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
+ mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
+ if (mcp6x_spibar == ERROR_PTR)
+ return 1;
+
+ status = mmio_readw(mcp6x_spibar + 0x530);
+ msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
+ status, (status >> MCP6X_SPI_REQUEST) & 0x1,
+ (status >> MCP6X_SPI_GRANT) & 0x1);
+ mcp_gpiostate = status & 0xff;
+
+ if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) {
+ /* This should never happen. */
+ msg_perr("MCP6X bitbang SPI master init failed!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/mstarddc_spi.c b/mstarddc_spi.c
new file mode 100644
index 0000000..809d690
--- /dev/null
+++ b/mstarddc_spi.c
@@ -0,0 +1,237 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2014 Alexandre Boeglin <alex@boeglin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if CONFIG_MSTARDDC_SPI == 1
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+#include <linux/i2c.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+
+static const struct spi_master spi_master_mstarddc;
+
+static int mstarddc_fd;
+static int mstarddc_addr;
+static int mstarddc_doreset = 1;
+
+// MSTAR DDC Commands
+#define MSTARDDC_SPI_WRITE 0x10
+#define MSTARDDC_SPI_READ 0x11
+#define MSTARDDC_SPI_END 0x12
+#define MSTARDDC_SPI_RESET 0x24
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int mstarddc_spi_shutdown(void *data)
+{
+ // Reset, disables ISP mode
+ if (mstarddc_doreset == 1) {
+ uint8_t cmd = MSTARDDC_SPI_RESET;
+ if (write(mstarddc_fd, &cmd, 1) < 0) {
+ msg_perr("Error sending reset command: errno %d.\n",
+ errno);
+ return -1;
+ }
+ } else {
+ msg_pinfo("Info: Reset command was not sent. "
+ "Either the noreset=1 option was used, "
+ "or an error occured.\n");
+ }
+
+ if (close(mstarddc_fd) < 0) {
+ msg_perr("Error closing device: errno %d.\n", errno);
+ return -1;
+ }
+ return 0;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+int mstarddc_spi_init(void)
+{
+ int ret = 0;
+
+ // Get device, address from command-line
+ char *i2c_device = extract_programmer_param("dev");
+ if (i2c_device != NULL && strlen(i2c_device) > 0) {
+ char *i2c_address = strchr(i2c_device, ':');
+ if (i2c_address != NULL) {
+ *i2c_address = '\0';
+ i2c_address++;
+ }
+ if (i2c_address == NULL || strlen(i2c_address) == 0) {
+ msg_perr("Error: no address specified.\n"
+ "Use flashrom -p mstarddc_spi:dev=/dev/device:address.\n");
+ ret = -1;
+ goto out;
+ }
+ mstarddc_addr = strtol(i2c_address, NULL, 16); // FIXME: error handling
+ } else {
+ msg_perr("Error: no device specified.\n"
+ "Use flashrom -p mstarddc_spi:dev=/dev/device:address.\n");
+ ret = -1;
+ goto out;
+ }
+ msg_pinfo("Info: Will try to use device %s and address 0x%02x.\n", i2c_device, mstarddc_addr);
+
+ // Get noreset=1 option from command-line
+ char *noreset = extract_programmer_param("noreset");
+ if (noreset != NULL && noreset[0] == '1')
+ mstarddc_doreset = 0;
+ free(noreset);
+ msg_pinfo("Info: Will %sreset the device at the end.\n", mstarddc_doreset ? "" : "NOT ");
+ // Open device
+ if ((mstarddc_fd = open(i2c_device, O_RDWR)) < 0) {
+ switch (errno) {
+ case EACCES:
+ msg_perr("Error opening %s: Permission denied.\n"
+ "Please use sudo or run as root.\n",
+ i2c_device);
+ break;
+ case ENOENT:
+ msg_perr("Error opening %s: No such file.\n"
+ "Please check you specified the correct device.\n",
+ i2c_device);
+ break;
+ default:
+ msg_perr("Error opening %s: %s.\n", i2c_device, strerror(errno));
+ }
+ ret = -1;
+ goto out;
+ }
+ // Set slave address
+ if (ioctl(mstarddc_fd, I2C_SLAVE, mstarddc_addr) < 0) {
+ msg_perr("Error setting slave address 0x%02x: errno %d.\n",
+ mstarddc_addr, errno);
+ ret = -1;
+ goto out;
+ }
+ // Enable ISP mode
+ uint8_t cmd[5] = { 'M', 'S', 'T', 'A', 'R' };
+ if (write(mstarddc_fd, cmd, 5) < 0) {
+ int enable_err = errno;
+ uint8_t end_cmd = MSTARDDC_SPI_END;
+
+ // Assume device is already in ISP mode, try to send END command
+ if (write(mstarddc_fd, &end_cmd, 1) < 0) {
+ msg_perr("Error enabling ISP mode: errno %d & %d.\n"
+ "Please check that device (%s) and address (0x%02x) are correct.\n",
+ enable_err, errno, i2c_device, mstarddc_addr);
+ ret = -1;
+ goto out;
+ }
+ }
+ // Register shutdown function
+ register_shutdown(mstarddc_spi_shutdown, NULL);
+
+ // Register programmer
+ register_spi_master(&spi_master_mstarddc);
+out:
+ free(i2c_device);
+ return ret;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int mstarddc_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int ret = 0;
+ uint8_t *cmd = malloc((writecnt + 1) * sizeof(uint8_t));
+ if (cmd == NULL) {
+ msg_perr("Error allocating memory: errno %d.\n", errno);
+ ret = -1;
+ }
+
+ if (!ret && writecnt) {
+ cmd[0] = MSTARDDC_SPI_WRITE;
+ memcpy(cmd + 1, writearr, writecnt);
+ if (write(mstarddc_fd, cmd, writecnt + 1) < 0) {
+ msg_perr("Error sending write command: errno %d.\n",
+ errno);
+ ret = -1;
+ }
+ }
+
+ if (!ret && readcnt) {
+ struct i2c_rdwr_ioctl_data i2c_data;
+ struct i2c_msg msg[2];
+
+ cmd[0] = MSTARDDC_SPI_READ;
+ i2c_data.nmsgs = 2;
+ i2c_data.msgs = msg;
+ i2c_data.msgs[0].addr = mstarddc_addr;
+ i2c_data.msgs[0].len = 1;
+ i2c_data.msgs[0].flags = 0;
+ i2c_data.msgs[0].buf = cmd;
+ i2c_data.msgs[1].addr = mstarddc_addr;
+ i2c_data.msgs[1].len = readcnt;
+ i2c_data.msgs[1].flags = I2C_M_RD;
+ i2c_data.msgs[1].buf = readarr;
+
+ if (ioctl(mstarddc_fd, I2C_RDWR, &i2c_data) < 0) {
+ msg_perr("Error sending read command: errno %d.\n",
+ errno);
+ ret = -1;
+ }
+ }
+
+ if (!ret && (writecnt || readcnt)) {
+ cmd[0] = MSTARDDC_SPI_END;
+ if (write(mstarddc_fd, cmd, 1) < 0) {
+ msg_perr("Error sending end command: errno %d.\n",
+ errno);
+ ret = -1;
+ }
+ }
+
+ /* Do not reset if something went wrong, as it might prevent from
+ * retrying flashing. */
+ if (ret != 0)
+ mstarddc_doreset = 0;
+
+ if (cmd)
+ free(cmd);
+
+ return ret;
+}
+
+static const struct spi_master spi_master_mstarddc = {
+ .type = SPI_CONTROLLER_MSTARDDC,
+ .max_data_read = 256,
+ .max_data_write = 256,
+ .command = mstarddc_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+#endif
diff --git a/nic3com.c b/nic3com.c
new file mode 100644
index 0000000..4d4702a
--- /dev/null
+++ b/nic3com.c
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define BIOS_ROM_ADDR 0x04
+#define BIOS_ROM_DATA 0x08
+#define INT_STATUS 0x0e
+#define INTERNAL_CONFIG 0x00
+#define SELECT_REG_WINDOW 0x800
+
+#define PCI_VENDOR_ID_3COM 0x10b7
+
+static uint32_t io_base_addr = 0;
+static uint32_t internal_conf;
+static uint16_t id;
+
+const struct dev_entry nics_3com[] = {
+ /* 3C90xB */
+ {0x10b7, 0x9055, OK, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-TX"},
+ {0x10b7, 0x9001, NT, "3COM", "3C90xB: PCI 10/100 Mbps; shared 10BASE-T/100BASE-T4" },
+ {0x10b7, 0x9004, OK, "3COM", "3C90xB: PCI 10BASE-T (TPO)" },
+ {0x10b7, 0x9005, NT, "3COM", "3C90xB: PCI 10BASE-T/10BASE2/AUI (COMBO)" },
+ {0x10b7, 0x9006, NT, "3COM", "3C90xB: PCI 10BASE-T/10BASE2 (TPC)" },
+ {0x10b7, 0x900a, NT, "3COM", "3C90xB: PCI 10BASE-FL" },
+ {0x10b7, 0x905a, NT, "3COM", "3C90xB: PCI 10BASE-FX" },
+ {0x10b7, 0x9058, OK, "3COM", "3C905B: Cyclone 10/100/BNC" },
+
+ /* 3C905C */
+ {0x10b7, 0x9200, OK, "3COM", "3C905C: EtherLink 10/100 PCI (TX)" },
+
+ /* 3C980C */
+ {0x10b7, 0x9805, NT, "3COM", "3C980C: EtherLink Server 10/100 PCI (TX)" },
+
+ {0},
+};
+
+static void nic3com_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t nic3com_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_nic3com = {
+ .chip_readb = nic3com_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = nic3com_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+static int nic3com_shutdown(void *data)
+{
+ /* 3COM 3C90xB cards need a special fixup. */
+ if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
+ || id == 0x9006 || id == 0x900a || id == 0x905a || id == 0x9058) {
+ /* Select register window 3 and restore the receiver status. */
+ OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
+ OUTL(internal_conf, io_base_addr + INTERNAL_CONFIG);
+ }
+
+ return 0;
+}
+
+int nic3com_init(void)
+{
+ struct pci_dev *dev = NULL;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(nics_3com, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ id = dev->device_id;
+
+ /* 3COM 3C90xB cards need a special fixup. */
+ if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
+ || id == 0x9006 || id == 0x900a || id == 0x905a || id == 0x9058) {
+ /* Select register window 3 and save the receiver status. */
+ OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
+ internal_conf = INL(io_base_addr + INTERNAL_CONFIG);
+
+ /* Set receiver type to MII for full BIOS ROM access. */
+ OUTL((internal_conf & 0xf00fffff) | 0x00600000, io_base_addr);
+ }
+
+ /*
+ * The lowest 16 bytes of the I/O mapped register space of (most) 3COM
+ * cards form a 'register window' into one of multiple (usually 8)
+ * register banks. For 3C90xB/3C90xC we need register window/bank 0.
+ */
+ OUTW(SELECT_REG_WINDOW + 0, io_base_addr + INT_STATUS);
+
+ if (register_shutdown(nic3com_shutdown, NULL))
+ return 1;
+
+ max_rom_decode.parallel = 128 * 1024;
+ register_par_master(&par_master_nic3com, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void nic3com_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
+ OUTB(val, io_base_addr + BIOS_ROM_DATA);
+}
+
+static uint8_t nic3com_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
+ return INB(io_base_addr + BIOS_ROM_DATA);
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/nicintel.c b/nicintel.c
new file mode 100644
index 0000000..69b40d3
--- /dev/null
+++ b/nicintel.c
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Datasheet: http://download.intel.com/design/network/datashts/82559_Fast_Ethernet_Multifunction_PCI_Cardbus_Controller_Datasheet.pdf */
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+uint8_t *nicintel_bar;
+uint8_t *nicintel_control_bar;
+
+const struct dev_entry nics_intel[] = {
+ {PCI_VENDOR_ID_INTEL, 0x1209, NT, "Intel", "8255xER/82551IT Fast Ethernet Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x1229, OK, "Intel", "82557/8/9/0/1 Ethernet Pro 100"},
+
+ {0},
+};
+
+/* Arbitrary limit, taken from the datasheet I just had lying around.
+ * 128 kByte on the 82559 device. Or not. Depends on whom you ask.
+ */
+#define NICINTEL_MEMMAP_SIZE (128 * 1024)
+#define NICINTEL_MEMMAP_MASK (NICINTEL_MEMMAP_SIZE - 1)
+
+#define NICINTEL_CONTROL_MEMMAP_SIZE 0x10
+
+#define CSR_FCR 0x0c
+
+static void nicintel_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t nicintel_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_nicintel = {
+ .chip_readb = nicintel_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = nicintel_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int nicintel_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uintptr_t addr;
+
+ /* Needed only for PCI accesses on some platforms.
+ * FIXME: Refactor that into get_mem_perms/rget_io_perms/get_pci_perms?
+ */
+ if (rget_io_perms())
+ return 1;
+
+ /* FIXME: BAR2 is not available if the device uses the CardBus function. */
+ dev = pcidev_init(nics_intel, PCI_BASE_ADDRESS_2);
+ if (!dev)
+ return 1;
+
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2);
+ if (!addr)
+ return 1;
+
+ nicintel_bar = rphysmap("Intel NIC flash", addr, NICINTEL_MEMMAP_SIZE);
+ if (nicintel_bar == ERROR_PTR)
+ return 1;
+
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!addr)
+ return 1;
+
+ nicintel_control_bar = rphysmap("Intel NIC control/status reg", addr, NICINTEL_CONTROL_MEMMAP_SIZE);
+ if (nicintel_control_bar == ERROR_PTR)
+ return 1;
+
+ /* FIXME: This register is pretty undocumented in all publicly available
+ * documentation from Intel. Let me quote the complete info we have:
+ * "Flash Control Register: The Flash Control register allows the CPU to
+ * enable writes to an external Flash. The Flash Control Register is a
+ * 32-bit field that allows access to an external Flash device."
+ * Ah yes, we also know where it is, but we have absolutely _no_ idea
+ * what we should do with it. Write 0x0001 because we have nothing
+ * better to do with our time.
+ */
+ pci_rmmio_writew(0x0001, nicintel_control_bar + CSR_FCR);
+
+ max_rom_decode.parallel = NICINTEL_MEMMAP_SIZE;
+ register_par_master(&par_master_nicintel, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void nicintel_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ pci_mmio_writeb(val, nicintel_bar + (addr & NICINTEL_MEMMAP_MASK));
+}
+
+static uint8_t nicintel_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return pci_mmio_readb(nicintel_bar + (addr & NICINTEL_MEMMAP_MASK));
+}
diff --git a/nicintel_eeprom.c b/nicintel_eeprom.c
new file mode 100644
index 0000000..b5d4202
--- /dev/null
+++ b/nicintel_eeprom.c
@@ -0,0 +1,331 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2013 Ricardo Ribalda - Qtechnology A/S
+ * Copyright (C) 2011, 2014 Stefan Tauner
+ *
+ * Based on nicinctel_spi.c and ichspi.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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+/*
+ * Datasheet: Intel 82580 Quad/Dual Gigabit Ethernet LAN Controller Datasheet
+ * 3.3.1.4: General EEPROM Software Access
+ * 4.7: Access to shared resources (FIXME: we should probably use this semaphore interface)
+ * 7.4: Register Descriptions
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "flash.h"
+#include "spi.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define MEMMAP_SIZE (0x14 + 3) /* Only EEC and EERD are needed. */
+
+#define EEC 0x10 /* EEPROM/Flash Control Register */
+#define EERD 0x14 /* EEPROM Read Register */
+
+/* EPROM/Flash Control Register bits */
+#define EE_SCK 0
+#define EE_CS 1
+#define EE_SI 2
+#define EE_SO 3
+#define EE_REQ 6
+#define EE_GNT 7
+#define EE_PRES 8
+#define EE_SIZE 11
+#define EE_SIZE_MASK 0xf
+
+/* EEPROM Read Register bits */
+#define EERD_START 0
+#define EERD_DONE 1
+#define EERD_ADDR 2
+#define EERD_DATA 16
+
+#define BIT(x) (1<<x)
+#define EE_PAGE_MASK 0x3f
+
+static uint8_t *nicintel_eebar;
+static struct pci_dev *nicintel_pci;
+
+#define UNPROG_DEVICE 0x1509
+
+const struct dev_entry nics_intel_ee[] = {
+ {PCI_VENDOR_ID_INTEL, 0x150e, OK, "Intel", "82580 Quad Gigabit Ethernet Controller (Copper)"},
+ {PCI_VENDOR_ID_INTEL, 0x150f, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Fiber)"},
+ {PCI_VENDOR_ID_INTEL, 0x1510, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Backplane)"},
+ {PCI_VENDOR_ID_INTEL, 0x1511, NT , "Intel", "82580 Quad Gigabit Ethernet Controller (Ext. PHY)"},
+ {PCI_VENDOR_ID_INTEL, 0x1511, NT , "Intel", "82580 Dual Gigabit Ethernet Controller (Copper)"},
+ {PCI_VENDOR_ID_INTEL, UNPROG_DEVICE, OK, "Intel", "Unprogrammed 82580 Quad/Dual Gigabit Ethernet Controller"},
+ {0},
+};
+
+static int nicintel_ee_probe(struct flashctx *flash)
+{
+ if (nicintel_pci->device_id == UNPROG_DEVICE)
+ flash->chip->total_size = 16; /* Fall back to minimum supported size. */
+ else {
+ uint32_t tmp = pci_mmio_readl(nicintel_eebar + EEC);
+ tmp = ((tmp >> EE_SIZE) & EE_SIZE_MASK);
+ switch (tmp) {
+ case 7:
+ flash->chip->total_size = 16;
+ break;
+ case 8:
+ flash->chip->total_size = 32;
+ break;
+ default:
+ msg_cerr("Unsupported chip size 0x%x\n", tmp);
+ return 0;
+ }
+ }
+
+ flash->chip->page_size = EE_PAGE_MASK + 1;
+ flash->chip->tested = TEST_OK_PREW;
+ flash->chip->gran = write_gran_1byte_implicit_erase;
+ flash->chip->block_erasers->eraseblocks[0].size = (EE_PAGE_MASK + 1);
+ flash->chip->block_erasers->eraseblocks[0].count = (flash->chip->total_size * 1024) / (EE_PAGE_MASK + 1);
+
+ return 1;
+}
+
+static int nicintel_ee_read_word(unsigned int addr, uint16_t *data)
+{
+ uint32_t tmp = BIT(EERD_START) | (addr << EERD_ADDR);
+ pci_mmio_writel(tmp, nicintel_eebar + EERD);
+
+ /* Poll done flag. 10.000.000 cycles seem to be enough. */
+ uint32_t i;
+ for (i = 0; i < 10000000; i++) {
+ tmp = pci_mmio_readl(nicintel_eebar + EERD);
+ if (tmp & BIT(EERD_DONE)) {
+ *data = (tmp >> EERD_DATA) & 0xffff;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int nicintel_ee_read(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len)
+{
+ uint16_t data;
+
+ /* The NIC interface always reads 16 b words so we need to convert the address and handle odd address
+ * explicitly at the start (and also at the end in the loop below). */
+ if (addr & 1) {
+ if (nicintel_ee_read_word(addr / 2, &data))
+ return -1;
+ *buf++ = data & 0xff;
+ addr++;
+ len--;
+ }
+
+ while (len > 0) {
+ if (nicintel_ee_read_word(addr / 2, &data))
+ return -1;
+ *buf++ = data & 0xff;
+ addr++;
+ len--;
+ if (len > 0) {
+ *buf++ = (data >> 8) & 0xff;
+ addr++;
+ len--;
+ }
+ }
+
+ return 0;
+}
+
+static int nicintel_ee_bitset(int reg, int bit, bool val)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_eebar + reg);
+ if (val)
+ tmp |= BIT(bit);
+ else
+ tmp &= ~BIT(bit);
+ pci_mmio_writel(tmp, nicintel_eebar + reg);
+
+ return -1;
+}
+
+/* Shifts one byte out while receiving another one by bitbanging (denoted "direct access" in the datasheet). */
+static int nicintel_ee_bitbang(uint8_t mosi, uint8_t *miso)
+{
+ uint8_t out = 0x0;
+
+ int i;
+ for (i = 7; i >= 0; i--) {
+ nicintel_ee_bitset(EEC, EE_SI, mosi & BIT(i));
+ nicintel_ee_bitset(EEC, EE_SCK, 1);
+ if (miso != NULL) {
+ uint32_t tmp = pci_mmio_readl(nicintel_eebar + EEC);
+ if (tmp & BIT(EE_SO))
+ out |= BIT(i);
+ }
+ nicintel_ee_bitset(EEC, EE_SCK, 0);
+ }
+
+ if (miso != NULL)
+ *miso = out;
+
+ return 0;
+}
+
+/* Polls the WIP bit of the status register of the attached EEPROM via bitbanging. */
+static int nicintel_ee_ready(void)
+{
+ unsigned int i;
+ for (i = 0; i < 1000; i++) {
+ nicintel_ee_bitset(EEC, EE_CS, 0);
+
+ nicintel_ee_bitbang(JEDEC_RDSR, NULL);
+ uint8_t rdsr;
+ nicintel_ee_bitbang(0x00, &rdsr);
+
+ nicintel_ee_bitset(EEC, EE_CS, 1);
+ programmer_delay(1);
+ if (!(rdsr & SPI_SR_WIP)) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Requests direct access to the SPI pins. */
+static int nicintel_ee_req(void)
+{
+ uint32_t tmp;
+ nicintel_ee_bitset(EEC, EE_REQ, 1);
+
+ tmp = pci_mmio_readl(nicintel_eebar + EEC);
+ if (!(tmp & BIT(EE_GNT))) {
+ msg_perr("Enabling eeprom access failed.\n");
+ return 1;
+ }
+
+ nicintel_ee_bitset(EEC, EE_SCK, 0);
+ return 0;
+}
+
+static int nicintel_ee_write(struct flashctx *flash, const uint8_t *buf, unsigned int addr, unsigned int len)
+{
+ if (nicintel_ee_req())
+ return -1;
+
+ int ret = -1;
+ if (nicintel_ee_ready())
+ goto out;
+
+ while (len > 0) {
+ /* WREN */
+ nicintel_ee_bitset(EEC, EE_CS, 0);
+ nicintel_ee_bitbang(JEDEC_WREN, NULL);
+ nicintel_ee_bitset(EEC, EE_CS, 1);
+ programmer_delay(1);
+
+ /* data */
+ nicintel_ee_bitset(EEC, EE_CS, 0);
+ nicintel_ee_bitbang(JEDEC_BYTE_PROGRAM, NULL);
+ nicintel_ee_bitbang((addr >> 8) & 0xff, NULL);
+ nicintel_ee_bitbang(addr & 0xff, NULL);
+ while (len > 0) {
+ nicintel_ee_bitbang((buf) ? *buf++ : 0xff, NULL);
+ len--;
+ addr++;
+ if (!(addr & EE_PAGE_MASK))
+ break;
+ }
+ nicintel_ee_bitset(EEC, EE_CS, 1);
+ programmer_delay(1);
+ if (nicintel_ee_ready())
+ goto out;
+ }
+ ret = 0;
+out:
+ nicintel_ee_bitset(EEC, EE_REQ, 0); /* Give up direct access. */
+ return ret;
+}
+
+static int nicintel_ee_erase(struct flashctx *flash, unsigned int addr, unsigned int len)
+{
+ return nicintel_ee_write(flash, NULL, addr, len);
+}
+
+static const struct opaque_master opaque_master_nicintel_ee = {
+ .probe = nicintel_ee_probe,
+ .read = nicintel_ee_read,
+ .write = nicintel_ee_write,
+ .erase = nicintel_ee_erase,
+};
+
+static int nicintel_ee_shutdown(void *eecp)
+{
+ uint32_t old_eec = *(uint32_t *)eecp;
+ /* Request bitbanging and unselect the chip first to be safe. */
+ if (nicintel_ee_req() || nicintel_ee_bitset(EEC, EE_CS, 1))
+ return -1;
+
+ /* Try to restore individual bits we care about. */
+ int ret = nicintel_ee_bitset(EEC, EE_SCK, old_eec & BIT(EE_SCK));
+ ret |= nicintel_ee_bitset(EEC, EE_SI, old_eec & BIT(EE_SI));
+ ret |= nicintel_ee_bitset(EEC, EE_CS, old_eec & BIT(EE_CS));
+ /* REQ will be cleared by hardware anyway after 2 seconds of inactivity on the SPI pins (3.3.2.1). */
+ ret |= nicintel_ee_bitset(EEC, EE_REQ, old_eec & BIT(EE_REQ));
+
+ free(eecp);
+ return ret;
+}
+
+int nicintel_ee_init(void)
+{
+ if (rget_io_perms())
+ return 1;
+
+ struct pci_dev *dev = pcidev_init(nics_intel_ee, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ nicintel_eebar = rphysmap("Intel Gigabit NIC w/ SPI EEPROM", io_base_addr, MEMMAP_SIZE);
+ nicintel_pci = dev;
+ if (dev->device_id != UNPROG_DEVICE) {
+ uint32_t eec = pci_mmio_readl(nicintel_eebar + EEC);
+
+ /* C.f. 3.3.1.5 for the detection mechanism (maybe? contradicting the EE_PRES definition),
+ * and 3.3.1.7 for possible recovery. */
+ if (!(eec & BIT(EE_PRES))) {
+ msg_perr("Controller reports no EEPROM is present.\n");
+ return 1;
+ }
+
+ uint32_t *eecp = malloc(sizeof(uint32_t));
+ if (eecp == NULL)
+ return 1;
+ *eecp = eec;
+
+ if (register_shutdown(nicintel_ee_shutdown, eecp))
+ return 1;
+ }
+
+ return register_opaque_master(&opaque_master_nicintel_ee);
+}
diff --git a/nicintel_spi.c b/nicintel_spi.c
new file mode 100644
index 0000000..9195c79
--- /dev/null
+++ b/nicintel_spi.c
@@ -0,0 +1,235 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2010 Idwer Vollering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Datasheets:
+ * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's Manual
+ * 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and 82547xx
+ * http://www.intel.com/content/www/us/en/ethernet-controllers/pci-pci-x-family-gbe-controllers-software-dev-manual.html
+ *
+ * PCIe GbE Controllers Open Source Software Developer's Manual
+ * http://www.intel.com/content/www/us/en/ethernet-controllers/pcie-gbe-controllers-open-source-manual.html
+ *
+ * Intel 82574 Gigabit Ethernet Controller Family Datasheet
+ * http://www.intel.com/content/www/us/en/ethernet-controllers/82574l-gbe-controller-datasheet.html
+ *
+ * Intel 82599 10 GbE Controller Datasheet (331520)
+ * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define MEMMAP_SIZE getpagesize()
+
+/* EEPROM/Flash Control & Data Register */
+#define EECD 0x10
+/* Flash Access Register */
+#define FLA 0x1c
+
+/*
+ * Register bits of EECD.
+ * Table 13-6
+ *
+ * Bit 04, 05: FWE (Flash Write Enable Control)
+ * 00b = not allowed (on some cards this sends an erase command if bit 31 (FL_ER) of FLA is set)
+ * 01b = flash writes disabled
+ * 10b = flash writes enabled
+ * 11b = not allowed
+ */
+#define FLASH_WRITES_DISABLED 0x10 /* FWE: 10000b */
+#define FLASH_WRITES_ENABLED 0x20 /* FWE: 100000b */
+
+/* Flash Access register bits
+ * Table 13-9
+ */
+#define FL_SCK 0
+#define FL_CS 1
+#define FL_SI 2
+#define FL_SO 3
+#define FL_REQ 4
+#define FL_GNT 5
+/* Currently unused */
+// #define FL_BUSY 30
+// #define FL_ER 31
+
+uint8_t *nicintel_spibar;
+
+const struct dev_entry nics_intel_spi[] = {
+ {PCI_VENDOR_ID_INTEL, 0x105e, OK, "Intel", "82571EB Gigabit Ethernet Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x1076, OK, "Intel", "82541GI Gigabit Ethernet Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x107c, OK, "Intel", "82541PI Gigabit Ethernet Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10b9, OK, "Intel", "82572EI Gigabit Ethernet Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10d3, OK, "Intel", "82574L Gigabit Ethernet Controller"},
+
+ {PCI_VENDOR_ID_INTEL, 0x10d8, NT, "Intel", "82599 10 Gigabit Unprogrammed Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10f7, NT, "Intel", "82599 10 Gigabit KX4 Dual Port Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10f8, NT, "Intel", "82599 10 Gigabit Dual Port Backplane Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10f9, NT, "Intel", "82599 10 Gigabit CX4 Dual Port Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10fb, NT, "Intel", "82599 10-Gigabit SFI/SFP+ Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x10fc, OK, "Intel", "82599 10 Gigabit XAUI/BX4 Dual Port Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x1517, NT, "Intel", "82599 10 Gigabit KR Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x151c, NT, "Intel", "82599 10 Gigabit TN Network Controller"},
+ {PCI_VENDOR_ID_INTEL, 0x1529, NT, "Intel", "82599 10 Gigabit Dual Port Network Controller with FCoE"},
+ {PCI_VENDOR_ID_INTEL, 0x152a, NT, "Intel", "82599 10 Gigabit Dual Port Backplane Controller with FCoE"},
+ {PCI_VENDOR_ID_INTEL, 0x1557, NT, "Intel", "82599 10 Gigabit SFI Network Controller"},
+
+ {0},
+};
+
+static void nicintel_request_spibus(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp |= 1 << FL_REQ;
+ pci_mmio_writel(tmp, nicintel_spibar + FLA);
+
+ /* Wait until we are allowed to use the SPI bus. */
+ while (!(pci_mmio_readl(nicintel_spibar + FLA) & (1 << FL_GNT))) ;
+}
+
+static void nicintel_release_spibus(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp &= ~(1 << FL_REQ);
+ pci_mmio_writel(tmp, nicintel_spibar + FLA);
+}
+
+static void nicintel_bitbang_set_cs(int val)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp &= ~(1 << FL_CS);
+ tmp |= (val << FL_CS);
+ pci_mmio_writel(tmp, nicintel_spibar + FLA);
+}
+
+static void nicintel_bitbang_set_sck(int val)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp &= ~(1 << FL_SCK);
+ tmp |= (val << FL_SCK);
+ pci_mmio_writel(tmp, nicintel_spibar + FLA);
+}
+
+static void nicintel_bitbang_set_mosi(int val)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp &= ~(1 << FL_SI);
+ tmp |= (val << FL_SI);
+ pci_mmio_writel(tmp, nicintel_spibar + FLA);
+}
+
+static int nicintel_bitbang_get_miso(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(nicintel_spibar + FLA);
+ tmp = (tmp >> FL_SO) & 0x1;
+ return tmp;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_nicintel = {
+ .type = BITBANG_SPI_MASTER_NICINTEL,
+ .set_cs = nicintel_bitbang_set_cs,
+ .set_sck = nicintel_bitbang_set_sck,
+ .set_mosi = nicintel_bitbang_set_mosi,
+ .get_miso = nicintel_bitbang_get_miso,
+ .request_bus = nicintel_request_spibus,
+ .release_bus = nicintel_release_spibus,
+ .half_period = 1,
+};
+
+static int nicintel_spi_shutdown(void *data)
+{
+ uint32_t tmp;
+
+ /* Disable writes manually. See the comment about EECD in nicintel_spi_init() for details. */
+ tmp = pci_mmio_readl(nicintel_spibar + EECD);
+ tmp &= ~FLASH_WRITES_ENABLED;
+ tmp |= FLASH_WRITES_DISABLED;
+ pci_mmio_writel(tmp, nicintel_spibar + EECD);
+
+ return 0;
+}
+
+int nicintel_spi_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t tmp;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(nics_intel_spi, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ if (dev->device_id < 0x10d8) {
+ nicintel_spibar = rphysmap("Intel Gigabit NIC w/ SPI flash", io_base_addr,
+ MEMMAP_SIZE);
+ } else {
+ nicintel_spibar = rphysmap("Intel 10 Gigabit NIC w/ SPI flash", io_base_addr + 0x10000,
+ MEMMAP_SIZE);
+ }
+ if (nicintel_spibar == ERROR_PTR)
+ return 1;
+
+ /* Automatic restore of EECD on shutdown is not possible because EECD
+ * does not only contain FLASH_WRITES_DISABLED|FLASH_WRITES_ENABLED,
+ * but other bits with side effects as well. Those other bits must be
+ * left untouched.
+ */
+ tmp = pci_mmio_readl(nicintel_spibar + EECD);
+ tmp &= ~FLASH_WRITES_DISABLED;
+ tmp |= FLASH_WRITES_ENABLED;
+ pci_mmio_writel(tmp, nicintel_spibar + EECD);
+
+ /* test if FWE is really set to allow writes */
+ tmp = pci_mmio_readl(nicintel_spibar + EECD);
+ if ( (tmp & FLASH_WRITES_DISABLED) || !(tmp & FLASH_WRITES_ENABLED) ) {
+ msg_perr("Enabling flash write access failed.\n");
+ return 1;
+ }
+
+ if (register_shutdown(nicintel_spi_shutdown, NULL))
+ return 1;
+
+ if (register_spi_bitbang_master(&bitbang_spi_master_nicintel))
+ return 1;
+
+ return 0;
+}
diff --git a/nicnatsemi.c b/nicnatsemi.c
new file mode 100644
index 0000000..ce22c94
--- /dev/null
+++ b/nicnatsemi.c
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Andrew Morgan <ziltro@ziltro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_NATSEMI 0x100b
+
+#define BOOT_ROM_ADDR 0x50
+#define BOOT_ROM_DATA 0x54
+
+static uint32_t io_base_addr = 0;
+const struct dev_entry nics_natsemi[] = {
+ {0x100b, 0x0020, NT, "National Semiconductor", "DP83815/DP83816"},
+ {0x100b, 0x0022, NT, "National Semiconductor", "DP83820"},
+
+ {0},
+};
+
+static void nicnatsemi_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t nicnatsemi_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_nicnatsemi = {
+ .chip_readb = nicnatsemi_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = nicnatsemi_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+int nicnatsemi_init(void)
+{
+ struct pci_dev *dev = NULL;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(nics_natsemi, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ /* The datasheet shows address lines MA0-MA16 in one place and MA0-MA15
+ * in another. My NIC has MA16 connected to A16 on the boot ROM socket
+ * so I'm assuming it is accessible. If not then next line wants to be
+ * max_rom_decode.parallel = 65536; and the mask in the read/write
+ * functions below wants to be 0x0000FFFF.
+ */
+ max_rom_decode.parallel = 131072;
+ register_par_master(&par_master_nicnatsemi, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void nicnatsemi_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ OUTL((uint32_t)addr & 0x0001FFFF, io_base_addr + BOOT_ROM_ADDR);
+ /*
+ * The datasheet requires 32 bit accesses to this register, but it seems
+ * that requirement might only apply if the register is memory mapped.
+ * Bits 8-31 of this register are apparently don't care, and if this
+ * register is I/O port mapped, 8 bit accesses to the lowest byte of the
+ * register seem to work fine. Due to that, we ignore the advice in the
+ * data sheet.
+ */
+ OUTB(val, io_base_addr + BOOT_ROM_DATA);
+}
+
+static uint8_t nicnatsemi_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ OUTL(((uint32_t)addr & 0x0001FFFF), io_base_addr + BOOT_ROM_ADDR);
+ /*
+ * The datasheet requires 32 bit accesses to this register, but it seems
+ * that requirement might only apply if the register is memory mapped.
+ * Bits 8-31 of this register are apparently don't care, and if this
+ * register is I/O port mapped, 8 bit accesses to the lowest byte of the
+ * register seem to work fine. Due to that, we ignore the advice in the
+ * data sheet.
+ */
+ return INB(io_base_addr + BOOT_ROM_DATA);
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/nicrealtek.c b/nicrealtek.c
new file mode 100644
index 0000000..07910a8
--- /dev/null
+++ b/nicrealtek.c
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Joerg Fischer <turboj@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_VENDOR_ID_SMC1211 0x1113
+
+static uint32_t io_base_addr = 0;
+static int bios_rom_addr, bios_rom_data;
+
+const struct dev_entry nics_realtek[] = {
+ {0x10ec, 0x8139, OK, "Realtek", "RTL8139/8139C/8139C+"},
+ {0x10ec, 0x8169, NT, "Realtek", "RTL8169"},
+ {0x1113, 0x1211, OK, "SMC", "1211TX"}, /* RTL8139 clone */
+
+ {0},
+};
+
+static void nicrealtek_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static uint8_t nicrealtek_chip_readb(const struct flashctx *flash, const chipaddr addr);
+static const struct par_master par_master_nicrealtek = {
+ .chip_readb = nicrealtek_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = nicrealtek_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+static int nicrealtek_shutdown(void *data)
+{
+ /* FIXME: We forgot to disable software access again. */
+ return 0;
+}
+
+int nicrealtek_init(void)
+{
+ struct pci_dev *dev = NULL;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(nics_realtek, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ /* Beware, this ignores the vendor ID! */
+ switch (dev->device_id) {
+ case 0x8139: /* RTL8139 */
+ case 0x1211: /* SMC 1211TX */
+ default:
+ bios_rom_addr = 0xD4;
+ bios_rom_data = 0xD7;
+ break;
+ case 0x8169: /* RTL8169 */
+ bios_rom_addr = 0x30;
+ bios_rom_data = 0x33;
+ break;
+ }
+
+ if (register_shutdown(nicrealtek_shutdown, NULL))
+ return 1;
+
+ register_par_master(&par_master_nicrealtek, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void nicrealtek_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ /* Output addr and data, set WE to 0, set OE to 1, set CS to 0,
+ * enable software access.
+ */
+ OUTL(((uint32_t)addr & 0x01FFFF) | 0x0A0000 | (val << 24),
+ io_base_addr + bios_rom_addr);
+ /* Output addr and data, set WE to 1, set OE to 1, set CS to 1,
+ * enable software access.
+ */
+ OUTL(((uint32_t)addr & 0x01FFFF) | 0x1E0000 | (val << 24),
+ io_base_addr + bios_rom_addr);
+}
+
+static uint8_t nicrealtek_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ uint8_t val;
+
+ /* FIXME: Can we skip reading the old data and simply use 0? */
+ /* Read old data. */
+ val = INB(io_base_addr + bios_rom_data);
+ /* Output new addr and old data, set WE to 1, set OE to 0, set CS to 0,
+ * enable software access.
+ */
+ OUTL(((uint32_t)addr & 0x01FFFF) | 0x060000 | (val << 24),
+ io_base_addr + bios_rom_addr);
+
+ /* Read new data. */
+ val = INB(io_base_addr + bios_rom_data);
+ /* Output addr and new data, set WE to 1, set OE to 1, set CS to 1,
+ * enable software access.
+ */
+ OUTL(((uint32_t)addr & 0x01FFFF) | 0x1E0000 | (val << 24),
+ io_base_addr + bios_rom_addr);
+
+ return val;
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/ogp_spi.c b/ogp_spi.c
new file mode 100644
index 0000000..929ecd9
--- /dev/null
+++ b/ogp_spi.c
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Mark Marshall
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_OGP 0x1227
+
+/* These are the register addresses for the OGD1 / OGA1. If they are
+ * different for later versions of the hardware then we will need
+ * logic to select between the different hardware versions. */
+#define OGA1_XP10_BPROM_SI 0x0040 /* W */
+#define OGA1_XP10_BPROM_SO 0x0040 /* R */
+#define OGA1_XP10_BPROM_CE_BAR 0x0044 /* W */
+#define OGA1_XP10_BPROM_SCK 0x0048 /* W */
+#define OGA1_XP10_BPROM_REG_SEL 0x004C /* W */
+#define OGA1_XP10_CPROM_SI 0x0050 /* W */
+#define OGA1_XP10_CPROM_SO 0x0050 /* R */
+#define OGA1_XP10_CPROM_CE_BAR 0x0054 /* W */
+#define OGA1_XP10_CPROM_SCK 0x0058 /* W */
+#define OGA1_XP10_CPROM_REG_SEL 0x005C /* W */
+
+static uint8_t *ogp_spibar;
+
+static uint32_t ogp_reg_sel;
+static uint32_t ogp_reg_siso;
+static uint32_t ogp_reg__ce;
+static uint32_t ogp_reg_sck;
+
+const struct dev_entry ogp_spi[] = {
+ {PCI_VENDOR_ID_OGP, 0x0000, OK, "Open Graphics Project", "Development Board OGD1"},
+
+ {0},
+};
+
+static void ogp_request_spibus(void)
+{
+ pci_mmio_writel(1, ogp_spibar + ogp_reg_sel);
+}
+
+static void ogp_release_spibus(void)
+{
+ pci_mmio_writel(0, ogp_spibar + ogp_reg_sel);
+}
+
+static void ogp_bitbang_set_cs(int val)
+{
+ pci_mmio_writel(val, ogp_spibar + ogp_reg__ce);
+}
+
+static void ogp_bitbang_set_sck(int val)
+{
+ pci_mmio_writel(val, ogp_spibar + ogp_reg_sck);
+}
+
+static void ogp_bitbang_set_mosi(int val)
+{
+ pci_mmio_writel(val, ogp_spibar + ogp_reg_siso);
+}
+
+static int ogp_bitbang_get_miso(void)
+{
+ uint32_t tmp;
+
+ tmp = pci_mmio_readl(ogp_spibar + ogp_reg_siso);
+ return tmp & 0x1;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_ogp = {
+ .type = BITBANG_SPI_MASTER_OGP,
+ .set_cs = ogp_bitbang_set_cs,
+ .set_sck = ogp_bitbang_set_sck,
+ .set_mosi = ogp_bitbang_set_mosi,
+ .get_miso = ogp_bitbang_get_miso,
+ .request_bus = ogp_request_spibus,
+ .release_bus = ogp_release_spibus,
+ .half_period = 0,
+};
+
+int ogp_spi_init(void)
+{
+ struct pci_dev *dev = NULL;
+ char *type;
+
+ type = extract_programmer_param("rom");
+
+ if (!type) {
+ msg_perr("Please use flashrom -p ogp_spi:rom=... to specify "
+ "which flashchip you want to access.\n");
+ return 1;
+ } else if (!strcasecmp(type, "bprom") || !strcasecmp(type, "bios")) {
+ ogp_reg_sel = OGA1_XP10_BPROM_REG_SEL;
+ ogp_reg_siso = OGA1_XP10_BPROM_SI;
+ ogp_reg__ce = OGA1_XP10_BPROM_CE_BAR;
+ ogp_reg_sck = OGA1_XP10_BPROM_SCK;
+ } else if (!strcasecmp(type, "cprom") || !strcasecmp(type, "s3")) {
+ ogp_reg_sel = OGA1_XP10_CPROM_REG_SEL;
+ ogp_reg_siso = OGA1_XP10_CPROM_SI;
+ ogp_reg__ce = OGA1_XP10_CPROM_CE_BAR;
+ ogp_reg_sck = OGA1_XP10_CPROM_SCK;
+ } else {
+ msg_perr("Invalid or missing rom= parameter.\n");
+ free(type);
+ return 1;
+ }
+ free(type);
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(ogp_spi, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!io_base_addr)
+ return 1;
+
+ ogp_spibar = rphysmap("OGP registers", io_base_addr, 4096);
+ if (ogp_spibar == ERROR_PTR)
+ return 1;
+
+ if (register_spi_bitbang_master(&bitbang_spi_master_ogp))
+ return 1;
+
+ return 0;
+}
diff --git a/opaque.c b/opaque.c
new file mode 100644
index 0000000..a5a829c
--- /dev/null
+++ b/opaque.c
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011,2013,2014 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the opaque master framework.
+ * An opaque master is a master which does not provide direct access
+ * to the flash chip and which abstracts all flash chip properties into a
+ * master specific interface.
+ */
+
+#include <stdint.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+
+int probe_opaque(struct flashctx *flash)
+{
+ return flash->mst->opaque.probe(flash);
+}
+
+int read_opaque(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return flash->mst->opaque.read(flash, buf, start, len);
+}
+
+int write_opaque(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return flash->mst->opaque.write(flash, buf, start, len);
+}
+
+int erase_opaque(struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen)
+{
+ return flash->mst->opaque.erase(flash, blockaddr, blocklen);
+}
+
+int register_opaque_master(const struct opaque_master *mst)
+{
+ struct registered_master rmst;
+
+ if (!mst->probe || !mst->read || !mst->write || !mst->erase) {
+ msg_perr("%s called with incomplete master definition. "
+ "Please report a bug at flashrom@flashrom.org\n",
+ __func__);
+ return ERROR_FLASHROM_BUG;
+ }
+ rmst.buses_supported = BUS_PROG;
+ rmst.opaque = *mst;
+ return register_master(&rmst);
+}
diff --git a/os.h b/os.h
new file mode 100644
index 0000000..b59e087
--- /dev/null
+++ b/os.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Header file for OS checking.
+ */
+
+#include "platform.h"
+
+// Solaris
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+#define __FLASHROM_OS__ "SunOS"
+// OS X
+#elif defined(__MACH__) && defined(__APPLE__)
+#define __FLASHROM_OS__ "Darwin"
+// FreeBSD
+#elif defined(__FreeBSD__)
+#define __FLASHROM_OS__ "FreeBSD"
+// FreeBSD with glibc-based userspace (e.g. Debian/kFreeBSD)
+#elif defined(__FreeBSD_kernel__) && defined(__GLIBC__)
+#define __FLASHROM_OS__ "FreeBSD-glibc"
+// DragonFlyBSD
+#elif defined(__DragonFly__)
+#define __FLASHROM_OS__ "DragonFlyBSD"
+// NetBSD
+#elif defined(__NetBSD__)
+#define __FLASHROM_OS__ "NetBSD"
+// OpenBSD
+#elif defined(__OpenBSD__)
+#define __FLASHROM_OS__ "OpenBSD"
+// DJGPP
+#elif defined(__DJGPP__)
+#define __FLASHROM_OS__ "DOS"
+// MinGW (always has _WIN32 available)
+#elif defined(__MINGW32__)
+#define __FLASHROM_OS__ "MinGW"
+// Cygwin (usually without _WIN32)
+#elif defined( __CYGWIN__)
+#define __FLASHROM_OS__ "Cygwin"
+// libpayload
+#elif defined(__LIBPAYLOAD__)
+#define __FLASHROM_OS__ "libpayload"
+// GNU Hurd
+#elif defined(__gnu_hurd__)
+#define __FLASHROM_OS__ "Hurd"
+// Linux
+#elif defined(__linux__)
+ // There are various flags in use on Android apparently. __ANDROID__ seems to be the most trustworthy.
+ #if defined(__ANDROID__)
+ #define __FLASHROM_OS__ "Android"
+ #else
+ #define __FLASHROM_OS__ "Linux"
+ #endif
+#endif
+__FLASHROM_OS__
diff --git a/pcidev.c b/pcidev.c
new file mode 100644
index 0000000..2c78063
--- /dev/null
+++ b/pcidev.c
@@ -0,0 +1,333 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010, 2011 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+struct pci_access *pacc;
+
+enum pci_bartype {
+ TYPE_MEMBAR,
+ TYPE_IOBAR,
+ TYPE_ROMBAR,
+ TYPE_UNKNOWN
+};
+
+uintptr_t pcidev_readbar(struct pci_dev *dev, int bar)
+{
+ uint64_t addr;
+ uint32_t upperaddr;
+ uint8_t headertype;
+ uint16_t supported_cycles;
+ enum pci_bartype bartype = TYPE_UNKNOWN;
+
+
+ headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f;
+ msg_pspew("PCI header type 0x%02x\n", headertype);
+
+ /* Don't use dev->base_addr[x] (as value for 'bar'), won't work on older libpci. */
+ addr = pci_read_long(dev, bar);
+
+ /* Sanity checks. */
+ switch (headertype) {
+ case PCI_HEADER_TYPE_NORMAL:
+ switch (bar) {
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_1:
+ case PCI_BASE_ADDRESS_2:
+ case PCI_BASE_ADDRESS_3:
+ case PCI_BASE_ADDRESS_4:
+ case PCI_BASE_ADDRESS_5:
+ if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ bartype = TYPE_IOBAR;
+ else
+ bartype = TYPE_MEMBAR;
+ break;
+ case PCI_ROM_ADDRESS:
+ bartype = TYPE_ROMBAR;
+ break;
+ }
+ break;
+ case PCI_HEADER_TYPE_BRIDGE:
+ switch (bar) {
+ case PCI_BASE_ADDRESS_0:
+ case PCI_BASE_ADDRESS_1:
+ if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
+ bartype = TYPE_IOBAR;
+ else
+ bartype = TYPE_MEMBAR;
+ break;
+ case PCI_ROM_ADDRESS1:
+ bartype = TYPE_ROMBAR;
+ break;
+ }
+ break;
+ case PCI_HEADER_TYPE_CARDBUS:
+ break;
+ default:
+ msg_perr("Unknown PCI header type 0x%02x, BAR type cannot be determined reliably.\n",
+ headertype);
+ break;
+ }
+
+ supported_cycles = pci_read_word(dev, PCI_COMMAND);
+
+ msg_pdbg("Requested BAR is of type ");
+ switch (bartype) {
+ case TYPE_MEMBAR:
+ msg_pdbg("MEM");
+ if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
+ msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n");
+ /* TODO: Abort here? */
+ }
+ msg_pdbg(", %sbit, %sprefetchable\n",
+ ((addr & 0x6) == 0x0) ? "32" : (((addr & 0x6) == 0x4) ? "64" : "reserved"),
+ (addr & 0x8) ? "" : "not ");
+ if ((addr & 0x6) == 0x4) {
+ /* The spec says that a 64-bit register consumes
+ * two subsequent dword locations.
+ */
+ upperaddr = pci_read_long(dev, bar + 4);
+ if (upperaddr != 0x00000000) {
+ /* Fun! A real 64-bit resource. */
+ if (sizeof(uintptr_t) != sizeof(uint64_t)) {
+ msg_perr("BAR unreachable!");
+ /* TODO: Really abort here? If multiple PCI devices match,
+ * we might never tell the user about the other devices.
+ */
+ return 0;
+ }
+ addr |= (uint64_t)upperaddr << 32;
+ }
+ }
+ addr &= PCI_BASE_ADDRESS_MEM_MASK;
+ break;
+ case TYPE_IOBAR:
+ msg_pdbg("I/O\n");
+#if __FLASHROM_HAVE_OUTB__
+ if (!(supported_cycles & PCI_COMMAND_IO)) {
+ msg_perr("I/O BAR access requested, but device has I/O space accesses disabled.\n");
+ /* TODO: Abort here? */
+ }
+#else
+ msg_perr("I/O BAR access requested, but flashrom does not support I/O BAR access on this "
+ "platform (yet).\n");
+#endif
+ addr &= PCI_BASE_ADDRESS_IO_MASK;
+ break;
+ case TYPE_ROMBAR:
+ msg_pdbg("ROM\n");
+ /* Not sure if this check is needed. */
+ if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
+ msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n");
+ /* TODO: Abort here? */
+ }
+ addr &= PCI_ROM_ADDRESS_MASK;
+ break;
+ case TYPE_UNKNOWN:
+ msg_perr("BAR type unknown, please report a bug at flashrom@flashrom.org\n");
+ }
+
+ return (uintptr_t)addr;
+}
+
+static int pcidev_shutdown(void *data)
+{
+ if (pacc == NULL) {
+ msg_perr("%s: Tried to cleanup an invalid PCI context!\n"
+ "Please report a bug at flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+ pci_cleanup(pacc);
+ return 0;
+}
+
+int pci_init_common(void)
+{
+ if (pacc != NULL) {
+ msg_perr("%s: Tried to allocate a new PCI context, but there is still an old one!\n"
+ "Please report a bug at flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+ pacc = pci_alloc(); /* Get the pci_access structure */
+ pci_init(pacc); /* Initialize the PCI library */
+ if (register_shutdown(pcidev_shutdown, NULL))
+ return 1;
+ pci_scan_bus(pacc); /* We want to get the list of devices */
+ return 0;
+}
+
+/* pcidev_init gets an array of allowed PCI device IDs and returns a pointer to struct pci_dev iff exactly one
+ * match was found. If the "pci=bb:dd.f" programmer parameter was specified, a match is only considered if it
+ * also matches the specified bus:device.function.
+ * For convenience, this function also registers its own undo handlers.
+ */
+struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar)
+{
+ struct pci_dev *dev;
+ struct pci_dev *found_dev = NULL;
+ struct pci_filter filter;
+ char *pcidev_bdf;
+ char *msg = NULL;
+ int found = 0;
+ int i;
+ uintptr_t addr = 0;
+
+ if (pci_init_common() != 0)
+ return NULL;
+ pci_filter_init(pacc, &filter);
+
+ /* Filter by bb:dd.f (if supplied by the user). */
+ pcidev_bdf = extract_programmer_param("pci");
+ if (pcidev_bdf != NULL) {
+ if ((msg = pci_filter_parse_slot(&filter, pcidev_bdf))) {
+ msg_perr("Error: %s\n", msg);
+ return NULL;
+ }
+ }
+ free(pcidev_bdf);
+
+ for (dev = pacc->devices; dev; dev = dev->next) {
+ if (pci_filter_match(&filter, dev)) {
+ /* Check against list of supported devices. */
+ for (i = 0; devs[i].device_name != NULL; i++)
+ if ((dev->vendor_id == devs[i].vendor_id) &&
+ (dev->device_id == devs[i].device_id))
+ break;
+ /* Not supported, try the next one. */
+ if (devs[i].device_name == NULL)
+ continue;
+
+ msg_pdbg("Found \"%s %s\" (%04x:%04x, BDF %02x:%02x.%x).\n", devs[i].vendor_name,
+ devs[i].device_name, dev->vendor_id, dev->device_id, dev->bus, dev->dev,
+ dev->func);
+ if (devs[i].status == NT)
+ msg_pinfo("===\nThis PCI device is UNTESTED. Please report the 'flashrom -p "
+ "xxxx' output \n"
+ "to flashrom@flashrom.org if it works for you. Please add the name "
+ "of your\n"
+ "PCI device to the subject. Thank you for your help!\n===\n");
+
+ /* FIXME: We should count all matching devices, not
+ * just those with a valid BAR.
+ */
+ if ((addr = pcidev_readbar(dev, bar)) != 0) {
+ found_dev = dev;
+ found++;
+ }
+ }
+ }
+
+ /* Only continue if exactly one supported PCI dev has been found. */
+ if (found == 0) {
+ msg_perr("Error: No supported PCI device found.\n");
+ return NULL;
+ } else if (found > 1) {
+ msg_perr("Error: Multiple supported PCI devices found. Use 'flashrom -p xxxx:pci=bb:dd.f' \n"
+ "to explicitly select the card with the given BDF (PCI bus, device, function).\n");
+ return NULL;
+ }
+
+ return found_dev;
+}
+
+enum pci_write_type {
+ pci_write_type_byte,
+ pci_write_type_word,
+ pci_write_type_long,
+};
+
+struct undo_pci_write_data {
+ struct pci_dev dev;
+ int reg;
+ enum pci_write_type type;
+ union {
+ uint8_t bytedata;
+ uint16_t worddata;
+ uint32_t longdata;
+ };
+};
+
+int undo_pci_write(void *p)
+{
+ struct undo_pci_write_data *data = p;
+ if (pacc == NULL) {
+ msg_perr("%s: Tried to undo PCI writes without a valid PCI context!\n"
+ "Please report a bug at flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+ msg_pdbg("Restoring PCI config space for %02x:%02x:%01x reg 0x%02x\n",
+ data->dev.bus, data->dev.dev, data->dev.func, data->reg);
+ switch (data->type) {
+ case pci_write_type_byte:
+ pci_write_byte(&data->dev, data->reg, data->bytedata);
+ break;
+ case pci_write_type_word:
+ pci_write_word(&data->dev, data->reg, data->worddata);
+ break;
+ case pci_write_type_long:
+ pci_write_long(&data->dev, data->reg, data->longdata);
+ break;
+ }
+ /* p was allocated in register_undo_pci_write. */
+ free(p);
+ return 0;
+}
+
+#define register_undo_pci_write(a, b, c) \
+{ \
+ struct undo_pci_write_data *undo_pci_write_data; \
+ undo_pci_write_data = malloc(sizeof(struct undo_pci_write_data)); \
+ if (!undo_pci_write_data) { \
+ msg_gerr("Out of memory!\n"); \
+ exit(1); \
+ } \
+ undo_pci_write_data->dev = *a; \
+ undo_pci_write_data->reg = b; \
+ undo_pci_write_data->type = pci_write_type_##c; \
+ undo_pci_write_data->c##data = pci_read_##c(dev, reg); \
+ register_shutdown(undo_pci_write, undo_pci_write_data); \
+}
+
+#define register_undo_pci_write_byte(a, b) register_undo_pci_write(a, b, byte)
+#define register_undo_pci_write_word(a, b) register_undo_pci_write(a, b, word)
+#define register_undo_pci_write_long(a, b) register_undo_pci_write(a, b, long)
+
+int rpci_write_byte(struct pci_dev *dev, int reg, uint8_t data)
+{
+ register_undo_pci_write_byte(dev, reg);
+ return pci_write_byte(dev, reg, data);
+}
+
+int rpci_write_word(struct pci_dev *dev, int reg, uint16_t data)
+{
+ register_undo_pci_write_word(dev, reg);
+ return pci_write_word(dev, reg, data);
+}
+
+int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data)
+{
+ register_undo_pci_write_long(dev, reg);
+ return pci_write_long(dev, reg, data);
+}
diff --git a/physmap.c b/physmap.c
new file mode 100644
index 0000000..a261ccd
--- /dev/null
+++ b/physmap.c
@@ -0,0 +1,682 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Peter Stuge <peter@stuge.se>
+ * Copyright (C) 2009 coresystems GmbH
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2010 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+/* No file access needed/possible to get mmap access permissions or access MSR. */
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
+
+#ifdef __DJGPP__
+#include <dpmi.h>
+#include <sys/nearptr.h>
+
+#define MEM_DEV "dpmi"
+
+static void *realmem_map;
+
+static void *map_first_meg(uintptr_t phys_addr, size_t len)
+{
+ if (realmem_map)
+ return realmem_map + phys_addr;
+
+ realmem_map = valloc(1024 * 1024);
+
+ if (!realmem_map)
+ return ERROR_PTR;
+
+ if (__djgpp_map_physical_memory(realmem_map, (1024 * 1024), 0)) {
+ free(realmem_map);
+ realmem_map = NULL;
+ return ERROR_PTR;
+ }
+
+ return realmem_map + phys_addr;
+}
+
+static void *sys_physmap(uintptr_t phys_addr, size_t len)
+{
+ int ret;
+ __dpmi_meminfo mi;
+
+ /* Enable 4GB limit on DS descriptor. */
+ if (!__djgpp_nearptr_enable())
+ return ERROR_PTR;
+
+ if ((phys_addr + len - 1) < (1024 * 1024)) {
+ /* We need to use another method to map first 1MB. */
+ return map_first_meg(phys_addr, len);
+ }
+
+ mi.address = phys_addr;
+ mi.size = len;
+ ret = __dpmi_physical_address_mapping(&mi);
+
+ if (ret != 0)
+ return ERROR_PTR;
+
+ return (void *) mi.address + __djgpp_conventional_base;
+}
+
+#define sys_physmap_rw_uncached sys_physmap
+#define sys_physmap_ro_cached sys_physmap
+
+void sys_physunmap_unaligned(void *virt_addr, size_t len)
+{
+ __dpmi_meminfo mi;
+
+ /* There is no known way to unmap the first 1 MB. The DPMI server will
+ * do this for us on exit.
+ */
+ if ((virt_addr >= realmem_map) &&
+ ((virt_addr + len) <= (realmem_map + (1024 * 1024)))) {
+ return;
+ }
+
+ mi.address = (unsigned long) virt_addr;
+ __dpmi_free_physical_address_mapping(&mi);
+}
+
+#elif defined(__LIBPAYLOAD__)
+#include <arch/virtual.h>
+
+#define MEM_DEV ""
+
+void *sys_physmap(uintptr_t phys_addr, size_t len)
+{
+ return (void *)phys_to_virt(phys_addr);
+}
+
+#define sys_physmap_rw_uncached sys_physmap
+#define sys_physmap_ro_cached sys_physmap
+
+void sys_physunmap_unaligned(void *virt_addr, size_t len)
+{
+}
+#elif defined(__MACH__) && defined(__APPLE__)
+
+#define MEM_DEV "DirectHW"
+
+static void *sys_physmap(uintptr_t phys_addr, size_t len)
+{
+ /* The short form of ?: is a GNU extension.
+ * FIXME: map_physical returns NULL both for errors and for success
+ * if the region is mapped at virtual address zero. If in doubt, report
+ * an error until a better interface exists.
+ */
+ return map_physical(phys_addr, len) ? : ERROR_PTR;
+}
+
+/* The OS X driver does not differentiate between mapping types. */
+#define sys_physmap_rw_uncached sys_physmap
+#define sys_physmap_ro_cached sys_physmap
+
+void sys_physunmap_unaligned(void *virt_addr, size_t len)
+{
+ unmap_physical(virt_addr, len);
+}
+
+#else
+#include <sys/mman.h>
+
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+# define MEM_DEV "/dev/xsvc"
+#else
+# define MEM_DEV "/dev/mem"
+#endif
+
+static int fd_mem = -1;
+static int fd_mem_cached = -1;
+
+/* For MMIO access. Must be uncached, doesn't make sense to restrict to ro. */
+static void *sys_physmap_rw_uncached(uintptr_t phys_addr, size_t len)
+{
+ void *virt_addr;
+
+ if (-1 == fd_mem) {
+ /* Open the memory device UNCACHED. Important for MMIO. */
+ if (-1 == (fd_mem = open(MEM_DEV, O_RDWR | O_SYNC))) {
+ msg_perr("Critical error: open(" MEM_DEV "): %s\n", strerror(errno));
+ return ERROR_PTR;
+ }
+ }
+
+ virt_addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem, (off_t)phys_addr);
+ return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr;
+}
+
+/* For reading DMI/coreboot/whatever tables. We should never write, and we
+ * do not care about caching.
+ */
+static void *sys_physmap_ro_cached(uintptr_t phys_addr, size_t len)
+{
+ void *virt_addr;
+
+ if (-1 == fd_mem_cached) {
+ /* Open the memory device CACHED. */
+ if (-1 == (fd_mem_cached = open(MEM_DEV, O_RDWR))) {
+ msg_perr("Critical error: open(" MEM_DEV "): %s\n", strerror(errno));
+ return ERROR_PTR;
+ }
+ }
+
+ virt_addr = mmap(NULL, len, PROT_READ, MAP_SHARED, fd_mem_cached, (off_t)phys_addr);
+ return MAP_FAILED == virt_addr ? ERROR_PTR : virt_addr;
+}
+
+void sys_physunmap_unaligned(void *virt_addr, size_t len)
+{
+ munmap(virt_addr, len);
+}
+#endif
+
+#define PHYSM_RW 0
+#define PHYSM_RO 1
+#define PHYSM_NOCLEANUP 0
+#define PHYSM_CLEANUP 1
+#define PHYSM_EXACT 0
+#define PHYSM_ROUND 1
+
+/* Round start to nearest page boundary below and set len so that the resulting address range ends at the lowest
+ * possible page boundary where the original address range is still entirely contained. It returns the
+ * difference between the rounded start address and the original start address. */
+static uintptr_t round_to_page_boundaries(uintptr_t *start, size_t *len)
+{
+ uintptr_t page_size = getpagesize();
+ uintptr_t page_mask = ~(page_size-1);
+ uintptr_t end = *start + *len;
+ uintptr_t old_start = *start;
+ msg_gspew("page_size=%" PRIxPTR "\n", page_size);
+ msg_gspew("pre-rounding: start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n",
+ PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, end);
+ *start = *start & page_mask;
+ end = (end + page_size - 1) & page_mask;
+ *len = end - *start;
+ msg_gspew("post-rounding: start=0x%0*" PRIxPTR ", len=0x%zx, end=0x%0*" PRIxPTR "\n",
+ PRIxPTR_WIDTH, *start, *len, PRIxPTR_WIDTH, *start + *len);
+ return old_start - *start;
+}
+
+struct undo_physmap_data {
+ void *virt_addr;
+ size_t len;
+};
+
+static int undo_physmap(void *data)
+{
+ if (data == NULL) {
+ msg_perr("%s: tried to physunmap without valid data!\n", __func__);
+ return 1;
+ }
+ struct undo_physmap_data *d = data;
+ physunmap_unaligned(d->virt_addr, d->len);
+ free(data);
+ return 0;
+}
+
+static void *physmap_common(const char *descr, uintptr_t phys_addr, size_t len, bool readonly, bool autocleanup,
+ bool round)
+{
+ void *virt_addr;
+ uintptr_t offset = 0;
+
+ if (len == 0) {
+ msg_pspew("Not mapping %s, zero size at 0x%0*" PRIxPTR ".\n", descr, PRIxPTR_WIDTH, phys_addr);
+ return ERROR_PTR;
+ }
+
+ if (round)
+ offset = round_to_page_boundaries(&phys_addr, &len);
+
+ if (readonly)
+ virt_addr = sys_physmap_ro_cached(phys_addr, len);
+ else
+ virt_addr = sys_physmap_rw_uncached(phys_addr, len);
+
+ if (ERROR_PTR == virt_addr) {
+ if (NULL == descr)
+ descr = "memory";
+ msg_perr("Error accessing %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n",
+ descr, len, PRIxPTR_WIDTH, phys_addr);
+ msg_perr(MEM_DEV " mmap failed: %s\n", strerror(errno));
+#ifdef __linux__
+ if (EINVAL == errno) {
+ msg_perr("In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n");
+ msg_perr("CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n");
+ msg_perr("Please check if either is enabled in your kernel before reporting a failure.\n");
+ msg_perr("You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n");
+ msg_perr("disabling the other option unfortunately requires a kernel recompile. Sorry!\n");
+ }
+#elif defined (__OpenBSD__)
+ msg_perr("Please set securelevel=-1 in /etc/rc.securelevel "
+ "and reboot, or reboot into\n"
+ "single user mode.\n");
+#endif
+ return ERROR_PTR;
+ }
+
+ if (autocleanup) {
+ struct undo_physmap_data *d = malloc(sizeof(struct undo_physmap_data));
+ if (d == NULL) {
+ msg_perr("%s: Out of memory!\n", __func__);
+ physunmap_unaligned(virt_addr, len);
+ return ERROR_PTR;
+ }
+
+ d->virt_addr = virt_addr;
+ d->len = len;
+ if (register_shutdown(undo_physmap, d) != 0) {
+ msg_perr("%s: Could not register shutdown function!\n", __func__);
+ physunmap_unaligned(virt_addr, len);
+ return ERROR_PTR;
+ }
+ }
+
+ return virt_addr + offset;
+}
+
+void physunmap_unaligned(void *virt_addr, size_t len)
+{
+ /* No need to check for zero size, such mappings would have yielded ERROR_PTR. */
+ if (virt_addr == ERROR_PTR) {
+ msg_perr("Trying to unmap a nonexisting mapping!\n"
+ "Please report a bug at flashrom@flashrom.org\n");
+ return;
+ }
+
+ sys_physunmap_unaligned(virt_addr, len);
+}
+
+void physunmap(void *virt_addr, size_t len)
+{
+ uintptr_t tmp;
+
+ /* No need to check for zero size, such mappings would have yielded ERROR_PTR. */
+ if (virt_addr == ERROR_PTR) {
+ msg_perr("Trying to unmap a nonexisting mapping!\n"
+ "Please report a bug at flashrom@flashrom.org\n");
+ return;
+ }
+ tmp = (uintptr_t)virt_addr;
+ /* We assume that the virtual address of a page-aligned physical address is page-aligned as well. By
+ * extension, rounding a virtual unaligned address as returned by physmap should yield the same offset
+ * between rounded and original virtual address as between rounded and original physical address.
+ */
+ round_to_page_boundaries(&tmp, &len);
+ virt_addr = (void *)tmp;
+ physunmap_unaligned(virt_addr, len);
+}
+
+void *physmap(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_NOCLEANUP, PHYSM_ROUND);
+}
+
+void *rphysmap(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ return physmap_common(descr, phys_addr, len, PHYSM_RW, PHYSM_CLEANUP, PHYSM_ROUND);
+}
+
+void *physmap_ro(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_ROUND);
+}
+
+void *physmap_ro_unaligned(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ return physmap_common(descr, phys_addr, len, PHYSM_RO, PHYSM_NOCLEANUP, PHYSM_EXACT);
+}
+
+/* MSR abstraction implementations for Linux, OpenBSD, FreeBSD/Dragonfly, OSX, libpayload
+ * and a non-working default implemenation on the bottom. See also hwaccess.h for some (re)declarations. */
+#if defined(__i386__) || defined(__x86_64__)
+
+#ifdef __linux__
+/*
+ * Reading and writing to MSRs, however requires instructions rdmsr/wrmsr,
+ * which are ring0 privileged instructions so only the kernel can do the
+ * read/write. This function, therefore, requires that the msr kernel module
+ * be loaded to access these instructions from user space using device
+ * /dev/cpu/0/msr.
+ */
+
+static int fd_msr = -1;
+
+msr_t rdmsr(int addr)
+{
+ uint32_t buf[2];
+ msr_t msr = { 0xffffffff, 0xffffffff };
+
+ if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) {
+ msg_perr("Could not lseek() MSR: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ if (read(fd_msr, buf, 8) == 8) {
+ msr.lo = buf[0];
+ msr.hi = buf[1];
+ return msr;
+ }
+
+ if (errno != EIO) {
+ // A severe error.
+ msg_perr("Could not read() MSR: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ return msr;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+ uint32_t buf[2];
+ buf[0] = msr.lo;
+ buf[1] = msr.hi;
+
+ if (lseek(fd_msr, (off_t) addr, SEEK_SET) == -1) {
+ msg_perr("Could not lseek() MSR: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ if (write(fd_msr, buf, 8) != 8 && errno != EIO) {
+ msg_perr("Could not write() MSR: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ /* Some MSRs must not be written. */
+ if (errno == EIO)
+ return -1;
+
+ return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+ char msrfilename[64];
+ memset(msrfilename, 0, sizeof(msrfilename));
+ snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu/%d/msr", cpu);
+
+ if (fd_msr != -1) {
+ msg_pinfo("MSR was already initialized\n");
+ return -1;
+ }
+
+ fd_msr = open(msrfilename, O_RDWR);
+
+ if (fd_msr < 0) {
+ msg_perr("Error while opening %s: %s\n", msrfilename, strerror(errno));
+ msg_pinfo("Did you run 'modprobe msr'?\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+ if (fd_msr == -1) {
+ msg_pinfo("No MSR initialized.\n");
+ return;
+ }
+
+ close(fd_msr);
+
+ /* Clear MSR file descriptor. */
+ fd_msr = -1;
+}
+#elif defined(__OpenBSD__) && defined (__i386__) /* This does only work for certain AMD Geode LX systems see amdmsr(4). */
+#include <sys/ioctl.h>
+#include <machine/amdmsr.h>
+
+static int fd_msr = -1;
+
+msr_t rdmsr(int addr)
+{
+ struct amdmsr_req args;
+
+ msr_t msr = { 0xffffffff, 0xffffffff };
+
+ args.addr = (uint32_t)addr;
+
+ if (ioctl(fd_msr, RDMSR, &args) < 0) {
+ msg_perr("Error while executing RDMSR ioctl: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ msr.lo = args.val & 0xffffffff;
+ msr.hi = args.val >> 32;
+
+ return msr;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+ struct amdmsr_req args;
+
+ args.addr = addr;
+ args.val = (((uint64_t)msr.hi) << 32) | msr.lo;
+
+ if (ioctl(fd_msr, WRMSR, &args) < 0) {
+ msg_perr("Error while executing WRMSR ioctl: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+ char msrfilename[64];
+ memset(msrfilename, 0, sizeof(msrfilename));
+ snprintf(msrfilename, sizeof(msrfilename), "/dev/amdmsr");
+
+ if (fd_msr != -1) {
+ msg_pinfo("MSR was already initialized\n");
+ return -1;
+ }
+
+ fd_msr = open(msrfilename, O_RDWR);
+
+ if (fd_msr < 0) {
+ msg_perr("Error while opening %s: %s\n", msrfilename, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+ if (fd_msr == -1) {
+ msg_pinfo("No MSR initialized.\n");
+ return;
+ }
+
+ close(fd_msr);
+
+ /* Clear MSR file descriptor. */
+ fd_msr = -1;
+}
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#include <sys/ioctl.h>
+
+typedef struct {
+ int msr;
+ uint64_t data;
+} cpu_msr_args_t;
+#define CPU_RDMSR _IOWR('c', 1, cpu_msr_args_t)
+#define CPU_WRMSR _IOWR('c', 2, cpu_msr_args_t)
+
+static int fd_msr = -1;
+
+msr_t rdmsr(int addr)
+{
+ cpu_msr_args_t args;
+
+ msr_t msr = { 0xffffffff, 0xffffffff };
+
+ args.msr = addr;
+
+ if (ioctl(fd_msr, CPU_RDMSR, &args) < 0) {
+ msg_perr("Error while executing CPU_RDMSR ioctl: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ msr.lo = args.data & 0xffffffff;
+ msr.hi = args.data >> 32;
+
+ return msr;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+ cpu_msr_args_t args;
+
+ args.msr = addr;
+ args.data = (((uint64_t)msr.hi) << 32) | msr.lo;
+
+ if (ioctl(fd_msr, CPU_WRMSR, &args) < 0) {
+ msg_perr("Error while executing CPU_WRMSR ioctl: %s\n", strerror(errno));
+ close(fd_msr);
+ exit(1);
+ }
+
+ return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+ char msrfilename[64];
+ memset(msrfilename, 0, sizeof(msrfilename));
+ snprintf(msrfilename, sizeof(msrfilename), "/dev/cpu%d", cpu);
+
+ if (fd_msr != -1) {
+ msg_pinfo("MSR was already initialized\n");
+ return -1;
+ }
+
+ fd_msr = open(msrfilename, O_RDWR);
+
+ if (fd_msr < 0) {
+ msg_perr("Error while opening %s: %s\n", msrfilename, strerror(errno));
+ msg_pinfo("Did you install ports/sysutils/devcpu?\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+ if (fd_msr == -1) {
+ msg_pinfo("No MSR initialized.\n");
+ return;
+ }
+
+ close(fd_msr);
+
+ /* Clear MSR file descriptor. */
+ fd_msr = -1;
+}
+
+#elif defined(__MACH__) && defined(__APPLE__)
+/* rdmsr() and wrmsr() are provided by DirectHW which needs neither setup nor cleanup. */
+int setup_cpu_msr(int cpu)
+{
+ // Always succeed for now
+ return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+ // Nothing, yet.
+}
+#elif defined(__LIBPAYLOAD__)
+msr_t libpayload_rdmsr(int addr)
+{
+ msr_t msr;
+ unsigned long long val = _rdmsr(addr);
+ msr.lo = val & 0xffffffff;
+ msr.hi = val >> 32;
+ return msr;
+}
+
+int libpayload_wrmsr(int addr, msr_t msr)
+{
+ _wrmsr(addr, msr.lo | ((unsigned long long)msr.hi << 32));
+ return 0;
+}
+
+int setup_cpu_msr(int cpu)
+{
+ return 0;
+}
+
+void cleanup_cpu_msr(void)
+{
+}
+#else
+/* default MSR implementation */
+msr_t rdmsr(int addr)
+{
+ msr_t ret = { 0xffffffff, 0xffffffff };
+
+ return ret;
+}
+
+int wrmsr(int addr, msr_t msr)
+{
+ return -1;
+}
+
+int setup_cpu_msr(int cpu)
+{
+ msg_pinfo("No MSR support for your OS yet.\n");
+ return -1;
+}
+
+void cleanup_cpu_msr(void)
+{
+ // Nothing, yet.
+}
+#endif // OS switches for MSR code
+#else // x86
+/* Does MSR exist on non-x86 architectures? */
+#endif // arch switches for MSR code
diff --git a/pickit2_spi.c b/pickit2_spi.c
new file mode 100644
index 0000000..102fe37
--- /dev/null
+++ b/pickit2_spi.c
@@ -0,0 +1,509 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2014 Justin Chevrier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Connections are as follows:
+ *
+ * +------+-----+----------+
+ * | SPI | Pin | PICkit2 |
+ * +------+-----+----------+
+ * | /CS | 1 | VPP/MCLR |
+ * | VCC | 2 | VDD |
+ * | GND | 3 | GND |
+ * | MISO | 4 | PGD |
+ * | SCLK | 5 | PDC |
+ * | MOSI | 6 | AUX |
+ * +------+-----+----------+
+ *
+ * Inspiration and some specifics of the interface came via the AVRDude
+ * PICkit2 code: https://github.com/steve-m/avrdude/blob/master/pickit2.c
+ */
+
+#include "platform.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#if IS_WINDOWS
+#include <lusb0_usb.h>
+#else
+#include <usb.h>
+#endif
+
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+const struct dev_entry devs_pickit2_spi[] = {
+ {0x04D8, 0x0033, OK, "Microchip", "PICkit 2"},
+
+ {}
+};
+
+static usb_dev_handle *pickit2_handle;
+
+/* Default USB transaction timeout in ms */
+#define DFLT_TIMEOUT 10000
+
+#define CMD_LENGTH 64
+#define ENDPOINT_OUT 0x01
+#define ENDPOINT_IN 0x81
+
+#define CMD_GET_VERSION 0x76
+#define CMD_SET_VDD 0xA0
+#define CMD_SET_VPP 0xA1
+#define CMD_READ_VDD_VPP 0xA3
+#define CMD_EXEC_SCRIPT 0xA6
+#define CMD_CLR_DLOAD_BUFF 0xA7
+#define CMD_DOWNLOAD_DATA 0xA8
+#define CMD_CLR_ULOAD_BUFF 0xA9
+#define CMD_UPLOAD_DATA 0xAA
+#define CMD_END_OF_BUFFER 0xAD
+
+#define SCR_SPI_READ_BUF 0xC5
+#define SCR_SPI_WRITE_BUF 0xC6
+#define SCR_SET_AUX 0xCF
+#define SCR_LOOP 0xE9
+#define SCR_SET_ICSP_CLK_PERIOD 0xEA
+#define SCR_SET_PINS 0xF3
+#define SCR_BUSY_LED_OFF 0xF4
+#define SCR_BUSY_LED_ON 0xF5
+#define SCR_MCLR_GND_OFF 0xF6
+#define SCR_MCLR_GND_ON 0xF7
+#define SCR_VPP_PWM_OFF 0xF8
+#define SCR_VPP_PWM_ON 0xF9
+#define SCR_VPP_OFF 0xFA
+#define SCR_VPP_ON 0xFB
+#define SCR_VDD_OFF 0xFE
+#define SCR_VDD_ON 0xFF
+
+/* Might be useful for other USB devices as well. static for now.
+ * device parameter allows user to specify one device of multiple installed */
+static struct usb_device *get_device_by_vid_pid(uint16_t vid, uint16_t pid, unsigned int device)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (dev = bus->devices; dev; dev = dev->next)
+ if ((dev->descriptor.idVendor == vid) &&
+ (dev->descriptor.idProduct == pid)) {
+ if (device == 0)
+ return dev;
+ device--;
+ }
+
+ return NULL;
+}
+
+static int pickit2_get_firmware_version(void)
+{
+ int ret;
+ uint8_t command[CMD_LENGTH] = {CMD_GET_VERSION, CMD_END_OF_BUFFER};
+
+ ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
+ ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
+
+ msg_pdbg("PICkit2 Firmware Version: %d.%d\n", (int)command[0], (int)command[1]);
+ if (ret != CMD_LENGTH) {
+ msg_perr("Command Get Firmware Version failed (%s)!\n", usb_strerror());
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pickit2_set_spi_voltage(int millivolt)
+{
+ double voltage_selector;
+ switch (millivolt) {
+ case 0:
+ /* Admittedly this one is an assumption. */
+ voltage_selector = 0;
+ break;
+ case 1800:
+ voltage_selector = 1.8;
+ break;
+ case 2500:
+ voltage_selector = 2.5;
+ break;
+ case 3500:
+ voltage_selector = 3.5;
+ break;
+ default:
+ msg_perr("Unknown voltage %i mV! Aborting.\n", millivolt);
+ return 1;
+ }
+ msg_pdbg("Setting SPI voltage to %u.%03u V\n", millivolt / 1000,
+ millivolt % 1000);
+
+ uint8_t command[CMD_LENGTH] = {
+ CMD_SET_VDD,
+ voltage_selector * 2048 + 672,
+ (voltage_selector * 2048 + 672) / 256,
+ voltage_selector * 36,
+ CMD_SET_VPP,
+ 0x40,
+ voltage_selector * 18.61,
+ voltage_selector * 13,
+ CMD_END_OF_BUFFER
+ };
+
+ int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
+
+ if (ret != CMD_LENGTH) {
+ msg_perr("Command Set Voltage failed (%s)!\n", usb_strerror());
+ return 1;
+ }
+
+ return 0;
+}
+
+struct pickit2_spispeeds {
+ const char *const name;
+ const int speed;
+};
+
+static const struct pickit2_spispeeds spispeeds[] = {
+ { "1M", 0x1 },
+ { "500k", 0x2 },
+ { "333k", 0x3 },
+ { "250k", 0x4 },
+ { NULL, 0x0 },
+};
+
+static int pickit2_set_spi_speed(unsigned int spispeed_idx)
+{
+ msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed_idx].name);
+
+ uint8_t command[CMD_LENGTH] = {
+ CMD_EXEC_SCRIPT,
+ 2,
+ SCR_SET_ICSP_CLK_PERIOD,
+ spispeed_idx,
+ CMD_END_OF_BUFFER
+ };
+
+ int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
+
+ if (ret != CMD_LENGTH) {
+ msg_perr("Command Set SPI Speed failed (%s)!\n", usb_strerror());
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pickit2_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr)
+{
+
+ /* Maximum number of bytes per transaction (including command overhead) is 64. Lets play it safe
+ * and always assume the worst case scenario of 20 bytes command overhead.
+ */
+ if (writecnt + readcnt + 20 > CMD_LENGTH) {
+ msg_perr("\nTotal packetsize (%i) is greater than 64 supported, aborting.\n",
+ writecnt + readcnt + 20);
+ return 1;
+ }
+
+ uint8_t buf[CMD_LENGTH] = {CMD_DOWNLOAD_DATA, writecnt};
+ int i = 2;
+ for (; i < writecnt + 2; i++) {
+ buf[i] = writearr[i - 2];
+ }
+
+ buf[i++] = CMD_CLR_ULOAD_BUFF;
+ buf[i++] = CMD_EXEC_SCRIPT;
+
+ /* Determine script length based on number of bytes to be read or written */
+ if (writecnt == 1 && readcnt == 1)
+ buf[i++] = 7;
+ else if (writecnt == 1 || readcnt == 1)
+ buf[i++] = 10;
+ else
+ buf[i++] = 13;
+
+ /* Assert CS# */
+ buf[i++] = SCR_VPP_OFF;
+ buf[i++] = SCR_MCLR_GND_ON;
+
+ buf[i++] = SCR_SPI_WRITE_BUF;
+
+ if (writecnt > 1) {
+ buf[i++] = SCR_LOOP;
+ buf[i++] = 1; /* Loop back one instruction */
+ buf[i++] = writecnt - 1; /* Number of times to loop */
+ }
+
+ if (readcnt)
+ buf[i++] = SCR_SPI_READ_BUF;
+
+ if (readcnt > 1) {
+ buf[i++] = SCR_LOOP;
+ buf[i++] = 1; /* Loop back one instruction */
+ buf[i++] = readcnt - 1; /* Number of times to loop */
+ }
+
+ /* De-assert CS# */
+ buf[i++] = SCR_MCLR_GND_OFF;
+ buf[i++] = SCR_VPP_PWM_ON;
+ buf[i++] = SCR_VPP_ON;
+
+ buf[i++] = CMD_UPLOAD_DATA;
+ buf[i++] = CMD_END_OF_BUFFER;
+
+ int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
+
+ if (ret != CMD_LENGTH) {
+ msg_perr("Send SPI failed, expected %i, got %i %s!\n", writecnt, ret, usb_strerror());
+ return 1;
+ }
+
+ if (readcnt) {
+ ret = usb_interrupt_read(pickit2_handle, ENDPOINT_IN, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
+
+ if (ret != CMD_LENGTH) {
+ msg_perr("Receive SPI failed, expected %i, got %i %s!\n", readcnt, ret, usb_strerror());
+ return 1;
+ }
+
+ /* First byte indicates number of bytes transferred from upload buffer */
+ if (buf[0] != readcnt) {
+ msg_perr("Unexpected number of bytes transferred, expected %i, got %i!\n",
+ readcnt, ret);
+ return 1;
+ }
+
+ /* Actual data starts at byte number two */
+ memcpy(readarr, &buf[1], readcnt);
+ }
+
+ return 0;
+}
+
+/* Copied from dediprog.c */
+/* Might be useful for other USB devices as well. static for now. */
+static int parse_voltage(char *voltage)
+{
+ char *tmp = NULL;
+ int i;
+ int millivolt = 0, fraction = 0;
+
+ if (!voltage || !strlen(voltage)) {
+ msg_perr("Empty voltage= specified.\n");
+ return -1;
+ }
+ millivolt = (int)strtol(voltage, &tmp, 0);
+ voltage = tmp;
+ /* Handle "," and "." as decimal point. Everything after it is assumed
+ * to be in decimal notation.
+ */
+ if ((*voltage == '.') || (*voltage == ',')) {
+ voltage++;
+ for (i = 0; i < 3; i++) {
+ fraction *= 10;
+ /* Don't advance if the current character is invalid,
+ * but continue multiplying.
+ */
+ if ((*voltage < '0') || (*voltage > '9'))
+ continue;
+ fraction += *voltage - '0';
+ voltage++;
+ }
+ /* Throw away remaining digits. */
+ voltage += strspn(voltage, "0123456789");
+ }
+ /* The remaining string must be empty or "mV" or "V". */
+ tolower_string(voltage);
+
+ /* No unit or "V". */
+ if ((*voltage == '\0') || !strncmp(voltage, "v", 1)) {
+ millivolt *= 1000;
+ millivolt += fraction;
+ } else if (!strncmp(voltage, "mv", 2) ||
+ !strncmp(voltage, "millivolt", 9)) {
+ /* No adjustment. fraction is discarded. */
+ } else {
+ /* Garbage at the end of the string. */
+ msg_perr("Garbage voltage= specified.\n");
+ return -1;
+ }
+ return millivolt;
+}
+
+static const struct spi_master spi_master_pickit2 = {
+ .type = SPI_CONTROLLER_PICKIT2,
+ .max_data_read = 40,
+ .max_data_write = 40,
+ .command = pickit2_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static int pickit2_shutdown(void *data)
+{
+ /* Set all pins to float and turn voltages off */
+ uint8_t command[CMD_LENGTH] = {
+ CMD_EXEC_SCRIPT,
+ 8,
+ SCR_SET_PINS,
+ 3, /* Bit-0=1(PDC In), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
+ SCR_SET_AUX,
+ 1, /* Bit-0=1(Aux In), Bit-1=0(Aux LL) */
+ SCR_MCLR_GND_OFF,
+ SCR_VPP_OFF,
+ SCR_VDD_OFF,
+ SCR_BUSY_LED_OFF,
+ CMD_END_OF_BUFFER
+ };
+
+ int ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)command, CMD_LENGTH, DFLT_TIMEOUT);
+
+ if (ret != CMD_LENGTH) {
+ msg_perr("Command Shutdown failed (%s)!\n", usb_strerror());
+ ret = 1;
+ }
+ if (usb_release_interface(pickit2_handle, 0) != 0) {
+ msg_perr("Could not release USB interface!\n");
+ ret = 1;
+ }
+ if (usb_close(pickit2_handle) != 0) {
+ msg_perr("Could not close USB device!\n");
+ ret = 1;
+ }
+ return ret;
+}
+
+int pickit2_spi_init(void)
+{
+ unsigned int usedevice = 0; // FIXME: Allow selecting one of multiple devices
+
+ uint8_t buf[CMD_LENGTH] = {
+ CMD_EXEC_SCRIPT,
+ 10, /* Script length */
+ SCR_SET_PINS,
+ 2, /* Bit-0=0(PDC Out), Bit-1=1(PGD In), Bit-2=0(PDC LL), Bit-3=0(PGD LL) */
+ SCR_SET_AUX,
+ 0, /* Bit-0=0(Aux Out), Bit-1=0(Aux LL) */
+ SCR_VDD_ON,
+ SCR_MCLR_GND_OFF, /* Let CS# float */
+ SCR_VPP_PWM_ON,
+ SCR_VPP_ON, /* Pull CS# high */
+ SCR_BUSY_LED_ON,
+ CMD_CLR_DLOAD_BUFF,
+ CMD_CLR_ULOAD_BUFF,
+ CMD_END_OF_BUFFER
+ };
+
+
+ int spispeed_idx = 0;
+ char *spispeed = extract_programmer_param("spispeed");
+ if (spispeed != NULL) {
+ int i = 0;
+ for (; spispeeds[i].name; i++) {
+ if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
+ spispeed_idx = i;
+ break;
+ }
+ }
+ if (spispeeds[i].name == NULL) {
+ msg_perr("Error: Invalid 'spispeed' value.\n");
+ free(spispeed);
+ return 1;
+ }
+ free(spispeed);
+ }
+
+ int millivolt = 3500;
+ char *voltage = extract_programmer_param("voltage");
+ if (voltage != NULL) {
+ millivolt = parse_voltage(voltage);
+ free(voltage);
+ if (millivolt < 0)
+ return 1;
+ }
+
+ /* Here comes the USB stuff */
+ usb_init();
+ (void)usb_find_busses();
+ (void)usb_find_devices();
+ const uint16_t vid = devs_pickit2_spi[0].vendor_id;
+ const uint16_t pid = devs_pickit2_spi[0].device_id;
+ struct usb_device *dev = get_device_by_vid_pid(vid, pid, usedevice);
+ if (dev == NULL) {
+ msg_perr("Could not find a PICkit2 on USB!\n");
+ return 1;
+ }
+ msg_pdbg("Found USB device (%04x:%04x).\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
+
+ pickit2_handle = usb_open(dev);
+ int ret = usb_set_configuration(pickit2_handle, 1);
+ if (ret != 0) {
+ msg_perr("Could not set USB device configuration: %i %s\n", ret, usb_strerror());
+ if (usb_close(pickit2_handle) != 0)
+ msg_perr("Could not close USB device!\n");
+ return 1;
+ }
+ ret = usb_claim_interface(pickit2_handle, 0);
+ if (ret != 0) {
+ msg_perr("Could not claim USB device interface %i: %i %s\n", 0, ret, usb_strerror());
+ if (usb_close(pickit2_handle) != 0)
+ msg_perr("Could not close USB device!\n");
+ return 1;
+ }
+
+ if (register_shutdown(pickit2_shutdown, NULL) != 0) {
+ return 1;
+ }
+
+ if (pickit2_get_firmware_version()) {
+ return 1;
+ }
+
+ /* Command Set SPI Speed */
+ if (pickit2_set_spi_speed(spispeed_idx)) {
+ return 1;
+ }
+
+ /* Command Set SPI Voltage */
+ msg_pdbg("Setting voltage to %i mV.\n", millivolt);
+ if (pickit2_set_spi_voltage(millivolt) != 0) {
+ return 1;
+ }
+
+ /* Perform basic setup.
+ * Configure pin directions and logic levels, turn Vdd on, turn busy LED on and clear buffers. */
+ ret = usb_interrupt_write(pickit2_handle, ENDPOINT_OUT, (char *)buf, CMD_LENGTH, DFLT_TIMEOUT);
+ if (ret != CMD_LENGTH) {
+ msg_perr("Command Setup failed (%s)!\n", usb_strerror());
+ return 1;
+ }
+
+ register_spi_master(&spi_master_pickit2);
+
+ return 0;
+}
diff --git a/platform.h b/platform.h
new file mode 100644
index 0000000..c5a52ef
--- /dev/null
+++ b/platform.h
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Header file to determine target OS and CPU architecture.
+ */
+
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__ 1
+
+// Helper defines for operating systems
+#define IS_LINUX (defined(__gnu_linux__) || defined(__linux__))
+#define IS_MACOSX (defined(__APPLE__) && defined(__MACH__)) /* yes, both. */
+#define IS_WINDOWS (defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__WINDOWS__))
+
+// Likewise for target architectures
+#if defined (__i386__) || defined (__x86_64__) || defined(__amd64__)
+ #define __FLASHROM_ARCH__ "x86"
+ #define IS_X86 1
+#elif defined (__mips) || defined (__mips__) || defined (__MIPS__) || defined (mips)
+ #define __FLASHROM_ARCH__ "mips"
+ #define IS_MIPS 1
+#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) || \
+ defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
+ defined(_ARCH_PPC64) || defined(__ppc)
+ #define __FLASHROM_ARCH__ "ppc"
+ #define IS_PPC 1
+#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_ARM) || defined(_M_ARM) || defined(__arm) || \
+ defined(__aarch64__)
+ #define __FLASHROM_ARCH__ "arm"
+ #define IS_ARM 1
+#elif defined (__sparc__) || defined (__sparc)
+ #define __FLASHROM_ARCH__ "sparc"
+ #define IS_SPARC 1
+#elif defined (__alpha__)
+ #define __FLASHROM_ARCH__ "alpha"
+ #define IS_ALPHA 1
+#elif defined (__hppa__) || defined (__hppa)
+ #define __FLASHROM_ARCH__ "hppa"
+ #define IS_HPPA 1
+#elif defined (__m68k__)
+ #define __FLASHROM_ARCH__ "m68k"
+ #define IS_M68K 1
+#elif defined (__sh__)
+ #define __FLASHROM_ARCH__ "sh"
+ #define IS_SH 1
+#elif defined(__s390__) || defined(__s390x__) || defined(__zarch__)
+ #define __FLASHROM_ARCH__ "s390"
+ #define IS_S390 1
+#endif
+
+#if !(IS_X86 || IS_MIPS || IS_PPC || IS_ARM || IS_SPARC || IS_ALPHA || IS_HPPA || IS_M68K || IS_SH || IS_S390)
+#error Unknown architecture
+#endif
+
+#endif /* !__PLATFORM_H__ */
diff --git a/pony_spi.c b/pony_spi.c
new file mode 100644
index 0000000..f8faeb3
--- /dev/null
+++ b/pony_spi.c
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2012 Virgil-Adrian Teaca
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Driver for serial programmers compatible with SI-Prog or AJAWe.
+ *
+ * See http://www.lancos.com/siprogsch.html for SI-Prog schematics and instructions.
+ * See http://www.ajawe.pl/ajawe0208.htm for AJAWe serial programmer documentation.
+ *
+ * Pin layout for SI-Prog-like hardware:
+ *
+ * MOSI <-------< DTR
+ * MISO >-------> CTS
+ * SCK <---+---< RTS
+ * +---> DSR
+ * CS# <-------< TXD
+ *
+ * and for the AJAWe serial programmer:
+ *
+ * MOSI <-------< DTR
+ * MISO >-------> CTS
+ * SCK <-------< RTS
+ * CS# <-------< TXD
+ *
+ * DCE >-------> DSR
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+
+#include "flash.h"
+#include "programmer.h"
+
+enum pony_type {
+ TYPE_SI_PROG,
+ TYPE_SERBANG,
+ TYPE_AJAWE
+};
+
+/* Pins for master->slave direction */
+static int pony_negate_cs = 1;
+static int pony_negate_sck = 0;
+static int pony_negate_mosi = 0;
+/* Pins for slave->master direction */
+static int pony_negate_miso = 0;
+
+static void pony_bitbang_set_cs(int val)
+{
+ if (pony_negate_cs)
+ val ^= 1;
+
+ sp_set_pin(PIN_TXD, val);
+}
+
+static void pony_bitbang_set_sck(int val)
+{
+ if (pony_negate_sck)
+ val ^= 1;
+
+ sp_set_pin(PIN_RTS, val);
+}
+
+static void pony_bitbang_set_mosi(int val)
+{
+ if (pony_negate_mosi)
+ val ^= 1;
+
+ sp_set_pin(PIN_DTR, val);
+}
+
+static int pony_bitbang_get_miso(void)
+{
+ int tmp = sp_get_pin(PIN_CTS);
+
+ if (pony_negate_miso)
+ tmp ^= 1;
+
+ return tmp;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_pony = {
+ .type = BITBANG_SPI_MASTER_PONY,
+ .set_cs = pony_bitbang_set_cs,
+ .set_sck = pony_bitbang_set_sck,
+ .set_mosi = pony_bitbang_set_mosi,
+ .get_miso = pony_bitbang_get_miso,
+ .half_period = 0,
+};
+
+static int pony_spi_shutdown(void *data)
+{
+ /* Shut down serial port communication */
+ int ret = serialport_shutdown(NULL);
+ if (ret)
+ msg_pdbg("Pony SPI shutdown failed.\n");
+ else
+ msg_pdbg("Pony SPI shutdown completed.\n");
+
+ return ret;
+}
+
+int pony_spi_init(void)
+{
+ int i, data_out;
+ char *arg = NULL;
+ enum pony_type type = TYPE_SI_PROG;
+ char *name;
+ int have_device = 0;
+ int have_prog = 0;
+
+ /* The parameter is in format "dev=/dev/device,type=serbang" */
+ arg = extract_programmer_param("dev");
+ if (arg && strlen(arg)) {
+ sp_fd = sp_openserport(arg, 9600);
+ if (sp_fd == SER_INV_FD) {
+ free(arg);
+ return 1;
+ }
+ if (register_shutdown(pony_spi_shutdown, NULL) != 0) {
+ free(arg);
+ serialport_shutdown(NULL);
+ return 1;
+ }
+ have_device++;
+ }
+ free(arg);
+
+ if (!have_device) {
+ msg_perr("Error: No valid device specified.\n"
+ "Use flashrom -p pony_spi:dev=/dev/device[,type=name]\n");
+ return 1;
+ }
+
+ arg = extract_programmer_param("type");
+ if (arg && !strcasecmp(arg, "serbang")) {
+ type = TYPE_SERBANG;
+ } else if (arg && !strcasecmp(arg, "si_prog")) {
+ type = TYPE_SI_PROG;
+ } else if (arg && !strcasecmp( arg, "ajawe")) {
+ type = TYPE_AJAWE;
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Error: Missing argument for programmer type.\n");
+ free(arg);
+ return 1;
+ } else if (arg){
+ msg_perr("Error: Invalid programmer type specified.\n");
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ /*
+ * Configure the serial port pins, depending on the used programmer.
+ */
+ switch (type) {
+ case TYPE_AJAWE:
+ pony_negate_cs = 1;
+ pony_negate_sck = 1;
+ pony_negate_mosi = 1;
+ pony_negate_miso = 1;
+ name = "AJAWe";
+ break;
+ case TYPE_SERBANG:
+ pony_negate_cs = 0;
+ pony_negate_sck = 0;
+ pony_negate_mosi = 0;
+ pony_negate_miso = 1;
+ name = "serbang";
+ break;
+ default:
+ case TYPE_SI_PROG:
+ pony_negate_cs = 1;
+ pony_negate_sck = 0;
+ pony_negate_mosi = 0;
+ pony_negate_miso = 0;
+ name = "SI-Prog";
+ break;
+ }
+ msg_pdbg("Using %s programmer pinout.\n", name);
+
+ /*
+ * Detect if there is a compatible hardware programmer connected.
+ */
+ pony_bitbang_set_cs(1);
+ pony_bitbang_set_sck(1);
+ pony_bitbang_set_mosi(1);
+
+ switch (type) {
+ case TYPE_AJAWE:
+ have_prog = 1;
+ break;
+ case TYPE_SI_PROG:
+ case TYPE_SERBANG:
+ default:
+ have_prog = 1;
+ /* We toggle RTS/SCK a few times and see if DSR changes too. */
+ for (i = 1; i <= 10; i++) {
+ data_out = i & 1;
+ sp_set_pin(PIN_RTS, data_out);
+ programmer_delay(1000);
+
+ /* If DSR does not change, we are not connected to what we think */
+ if (data_out != sp_get_pin(PIN_DSR)) {
+ have_prog = 0;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (!have_prog) {
+ msg_perr("No programmer compatible with %s detected.\n", name);
+ return 1;
+ }
+
+ if (register_spi_bitbang_master(&bitbang_spi_master_pony)) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/print.c b/print.c
new file mode 100644
index 0000000..08bad02
--- /dev/null
+++ b/print.c
@@ -0,0 +1,1180 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2011-2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+
+static const char *test_state_to_text(enum test_state test_state)
+{
+ switch (test_state) {
+ case OK: return "OK";
+ case BAD: return "Not working";
+ case NA: return "N/A";
+ case DEP: return "Config-dependent";
+ case NT:
+ default: return "Untested";
+ }
+}
+
+static int print_supported_chips(void)
+{
+ const char *delim = "/";
+ const int mintoklen = 5;
+ const int border = 2;
+ int i, chipcount = 0;
+ int maxvendorlen = strlen("Vendor") + 1;
+ int maxchiplen = strlen("Device") + 1;
+ int maxtypelen = strlen("Type") + 1;
+ const struct flashchip *chip;
+ char *s;
+ char *ven, *dev;
+ char *tmpven, *tmpdev, *tmpven_save, *tmpdev_save;
+ int tmpvenlen, tmpdevlen, curvenlen, curdevlen;
+
+ /* calculate maximum column widths and by iterating over all chips */
+ for (chip = flashchips; chip->name != NULL; chip++) {
+ /* Ignore generic entries. */
+ if (!strncmp(chip->vendor, "Unknown", 7) ||
+ !strncmp(chip->vendor, "Programmer", 10) ||
+ !strncmp(chip->name, "unknown", 7))
+ continue;
+ chipcount++;
+
+ /* Find maximum vendor length (respecting line splitting). */
+ tmpven = (char *)chip->vendor;
+ do {
+ /* and take minimum token lengths into account */
+ tmpvenlen = 0;
+ do {
+ tmpvenlen += strcspn(tmpven, delim);
+ /* skip to the address after the first token */
+ tmpven += tmpvenlen;
+ if (tmpven[0] == '\0')
+ break;
+ tmpven++;
+ } while (tmpvenlen < mintoklen);
+ maxvendorlen = max(maxvendorlen, tmpvenlen);
+ if (tmpven[0] == '\0')
+ break;
+ } while (1);
+
+ /* same for device name */
+ tmpdev = (char *)chip->name;
+ do {
+ tmpdevlen = 0;
+ do {
+ tmpdevlen += strcspn(tmpdev, delim);
+ tmpdev += tmpdevlen;
+ if (tmpdev[0] == '\0')
+ break;
+ tmpdev++;
+ } while (tmpdevlen < mintoklen);
+ maxchiplen = max(maxchiplen, tmpdevlen);
+ if (tmpdev[0] == '\0')
+ break;
+ } while (1);
+
+ s = flashbuses_to_text(chip->bustype);
+ maxtypelen = max(maxtypelen, strlen(s));
+ free(s);
+ }
+ maxvendorlen += border;
+ maxchiplen += border;
+ maxtypelen += border;
+
+ msg_ginfo("Supported flash chips (total: %d):\n\n", chipcount);
+ msg_ginfo("Vendor");
+ for (i = strlen("Vendor"); i < maxvendorlen; i++)
+ msg_ginfo(" ");
+ msg_ginfo("Device");
+ for (i = strlen("Device"); i < maxchiplen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Test");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+ msg_ginfo("Known");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+ msg_ginfo(" Size ");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Type");
+ for (i = strlen("Type"); i < maxtypelen; i++)
+ msg_ginfo(" ");
+ msg_gdbg("Voltage");
+ msg_ginfo("\n");
+
+ for (i = 0; i < maxvendorlen + maxchiplen; i++)
+ msg_ginfo(" ");
+ msg_ginfo("OK ");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+ msg_ginfo("Broken");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+ msg_ginfo("[kB] ");
+ for (i = 0; i < border + maxtypelen; i++)
+ msg_ginfo(" ");
+ msg_gdbg("range [V]");
+ msg_ginfo("\n\n");
+ msg_ginfo("(P = PROBE, R = READ, E = ERASE, W = WRITE, - = N/A)\n\n");
+
+ for (chip = flashchips; chip->name != NULL; chip++) {
+ /* Don't print generic entries. */
+ if (!strncmp(chip->vendor, "Unknown", 7) ||
+ !strncmp(chip->vendor, "Programmer", 10) ||
+ !strncmp(chip->name, "unknown", 7))
+ continue;
+
+ /* support for multiline vendor names:
+ * - make a copy of the original vendor name
+ * - use strok to put the first token in tmpven
+ * - keep track of the length of all tokens on the current line
+ * for ' '-padding in curvenlen
+ * - check if additional tokens should be printed on the current
+ * line
+ * - after all other values are printed print the surplus tokens
+ * on fresh lines
+ */
+ ven = malloc(strlen(chip->vendor) + 1);
+ if (ven == NULL) {
+ msg_gerr("Out of memory!\n");
+ return 1;
+ }
+ strcpy(ven, chip->vendor);
+
+ tmpven = strtok_r(ven, delim, &tmpven_save);
+ msg_ginfo("%s", tmpven);
+ curvenlen = strlen(tmpven);
+ while ((tmpven = strtok_r(NULL, delim, &tmpven_save)) != NULL) {
+ msg_ginfo("%s", delim);
+ curvenlen++;
+ tmpvenlen = strlen(tmpven);
+ if (tmpvenlen >= mintoklen)
+ break; /* big enough to be on its own line */
+ msg_ginfo("%s", tmpven);
+ curvenlen += tmpvenlen;
+ }
+
+ for (i = curvenlen; i < maxvendorlen; i++)
+ msg_ginfo(" ");
+
+ /* support for multiline device names as above */
+ dev = malloc(strlen(chip->name) + 1);
+ if (dev == NULL) {
+ msg_gerr("Out of memory!\n");
+ return 1;
+ }
+ strcpy(dev, chip->name);
+
+ tmpdev = strtok_r(dev, delim, &tmpdev_save);
+ msg_ginfo("%s", tmpdev);
+ curdevlen = strlen(tmpdev);
+ while ((tmpdev = strtok_r(NULL, delim, &tmpdev_save)) != NULL) {
+ msg_ginfo("%s", delim);
+ curdevlen++;
+ tmpdevlen = strlen(tmpdev);
+ if (tmpdevlen >= mintoklen)
+ break; /* big enough to be on its own line */
+ msg_ginfo("%s", tmpdev);
+ curdevlen += tmpdevlen;
+ }
+
+ for (i = curdevlen; i < maxchiplen; i++)
+ msg_ginfo(" ");
+
+ if (chip->tested.probe == OK)
+ msg_ginfo("P");
+ else if (chip->tested.probe == NA)
+ msg_ginfo("-");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.read == OK)
+ msg_ginfo("R");
+ else if (chip->tested.read == NA)
+ msg_ginfo("-");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.erase == OK)
+ msg_ginfo("E");
+ else if (chip->tested.erase == NA)
+ msg_ginfo("-");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.write == OK)
+ msg_ginfo("W");
+ else if (chip->tested.write == NA)
+ msg_ginfo("-");
+ else
+ msg_ginfo(" ");
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+
+ if (chip->tested.probe == BAD)
+ msg_ginfo("P");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.read == BAD)
+ msg_ginfo("R");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.erase == BAD)
+ msg_ginfo("E");
+ else
+ msg_ginfo(" ");
+ if (chip->tested.write == BAD)
+ msg_ginfo("W");
+ else
+ msg_ginfo(" ");
+ for (i = 0; i < border + 1; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("%6d", chip->total_size);
+ for (i = 0; i < border; i++)
+ msg_ginfo(" ");
+
+ s = flashbuses_to_text(chip->bustype);
+ msg_ginfo("%s", s);
+ for (i = strlen(s); i < maxtypelen; i++)
+ msg_ginfo(" ");
+ free(s);
+
+ if (chip->voltage.min == 0 && chip->voltage.max == 0)
+ msg_gdbg("no info");
+ else
+ msg_gdbg("%0.02f;%0.02f",
+ chip->voltage.min/(double)1000,
+ chip->voltage.max/(double)1000);
+
+ /* print surplus vendor and device name tokens */
+ while (tmpven != NULL || tmpdev != NULL) {
+ msg_ginfo("\n");
+ if (tmpven != NULL){
+ msg_ginfo("%s", tmpven);
+ curvenlen = strlen(tmpven);
+ while ((tmpven = strtok_r(NULL, delim, &tmpven_save)) != NULL) {
+ msg_ginfo("%s", delim);
+ curvenlen++;
+ tmpvenlen = strlen(tmpven);
+ /* big enough to be on its own line */
+ if (tmpvenlen >= mintoklen)
+ break;
+ msg_ginfo("%s", tmpven);
+ curvenlen += tmpvenlen;
+ }
+ } else
+ curvenlen = 0;
+
+ for (i = curvenlen; i < maxvendorlen; i++)
+ msg_ginfo(" ");
+
+ if (tmpdev != NULL){
+ msg_ginfo("%s", tmpdev);
+ curdevlen = strlen(tmpdev);
+ while ((tmpdev = strtok_r(NULL, delim, &tmpdev_save)) != NULL) {
+ msg_ginfo("%s", delim);
+ curdevlen++;
+ tmpdevlen = strlen(tmpdev);
+ /* big enough to be on its own line */
+ if (tmpdevlen >= mintoklen)
+ break;
+ msg_ginfo("%s", tmpdev);
+ curdevlen += tmpdevlen;
+ }
+ }
+ }
+ msg_ginfo("\n");
+ free(ven);
+ free(dev);
+ }
+
+ return 0;
+}
+
+#if CONFIG_INTERNAL == 1
+static void print_supported_chipsets(void)
+{
+ int i, chipsetcount = 0;
+ const struct penable *c = chipset_enables;
+ int maxvendorlen = strlen("Vendor") + 1;
+ int maxchipsetlen = strlen("Chipset") + 1;
+
+ for (c = chipset_enables; c->vendor_name != NULL; c++) {
+ chipsetcount++;
+ maxvendorlen = max(maxvendorlen, strlen(c->vendor_name));
+ maxchipsetlen = max(maxchipsetlen, strlen(c->device_name));
+ }
+ maxvendorlen++;
+ maxchipsetlen++;
+
+ msg_ginfo("Supported chipsets (total: %d):\n\n", chipsetcount);
+
+ msg_ginfo("Vendor");
+ for (i = strlen("Vendor"); i < maxvendorlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Chipset");
+ for (i = strlen("Chipset"); i < maxchipsetlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("PCI IDs Status\n\n");
+
+ for (c = chipset_enables; c->vendor_name != NULL; c++) {
+ msg_ginfo("%s", c->vendor_name);
+ for (i = 0; i < maxvendorlen - strlen(c->vendor_name); i++)
+ msg_ginfo(" ");
+ msg_ginfo("%s", c->device_name);
+ for (i = 0; i < maxchipsetlen - strlen(c->device_name); i++)
+ msg_ginfo(" ");
+ msg_ginfo("%04x:%04x %s\n", c->vendor_id, c->device_id,
+ test_state_to_text(c->status));
+ }
+}
+
+static void print_supported_boards_helper(const struct board_info *boards,
+ const char *devicetype)
+{
+ int i;
+ unsigned int boardcount_good = 0, boardcount_bad = 0, boardcount_nt = 0;
+ const struct board_match *e = board_matches;
+ const struct board_info *b = boards;
+ int maxvendorlen = strlen("Vendor") + 1;
+ int maxboardlen = strlen("Board") + 1;
+
+ for (b = boards; b->vendor != NULL; b++) {
+ maxvendorlen = max(maxvendorlen, strlen(b->vendor));
+ maxboardlen = max(maxboardlen, strlen(b->name));
+ if (b->working == OK)
+ boardcount_good++;
+ else if (b->working == NT)
+ boardcount_nt++;
+ else
+ boardcount_bad++;
+ }
+ maxvendorlen++;
+ maxboardlen++;
+
+ msg_ginfo("%d known %s (good: %d, untested: %d, bad: %d):\n\n",
+ boardcount_good + boardcount_nt + boardcount_bad,
+ devicetype, boardcount_good, boardcount_nt, boardcount_bad);
+
+ msg_ginfo("Vendor");
+ for (i = strlen("Vendor"); i < maxvendorlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Board");
+ for (i = strlen("Board"); i < maxboardlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Status Required value for\n");
+ for (i = 0; i < maxvendorlen + maxboardlen + strlen("Status "); i++)
+ msg_ginfo(" ");
+ msg_ginfo("-p internal:mainboard=\n");
+
+ for (b = boards; b->vendor != NULL; b++) {
+ msg_ginfo("%s", b->vendor);
+ for (i = 0; i < maxvendorlen - strlen(b->vendor); i++)
+ msg_ginfo(" ");
+ msg_ginfo("%s", b->name);
+ for (i = 0; i < maxboardlen - strlen(b->name); i++)
+ msg_ginfo(" ");
+
+ switch (b->working) {
+ case OK: msg_ginfo("OK "); break;
+ case NT: msg_ginfo("NT "); break;
+ case DEP: msg_ginfo("DEP "); break;
+ case NA: msg_ginfo("N/A "); break;
+ case BAD:
+ default: msg_ginfo("BAD "); break;
+ }
+
+ for (e = board_matches; e->vendor_name != NULL; e++) {
+ if (strcmp(e->vendor_name, b->vendor)
+ || strcmp(e->board_name, b->name))
+ continue;
+ if (e->lb_vendor == NULL)
+ msg_ginfo("(autodetected)");
+ else
+ msg_ginfo("%s:%s", e->lb_vendor,
+ e->lb_part);
+ }
+ msg_ginfo("\n");
+ }
+}
+#endif
+
+void print_supported_devs(const struct programmer_entry prog, const char *const type)
+{
+ const struct dev_entry *const devs = prog.devs.dev;
+ msg_ginfo("\nSupported %s devices for the %s programmer:\n", type, prog.name);
+ unsigned int maxvendorlen = strlen("Vendor") + 1;
+ unsigned int maxdevlen = strlen("Device") + 1;
+
+ unsigned int i;
+ for (i = 0; devs[i].vendor_name != NULL; i++) {
+ maxvendorlen = max(maxvendorlen, strlen(devs[i].vendor_name));
+ maxdevlen = max(maxdevlen, strlen(devs[i].device_name));
+ }
+ maxvendorlen++;
+ maxdevlen++;
+
+ msg_ginfo("Vendor");
+ for (i = strlen("Vendor"); i < maxvendorlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo("Device");
+ for (i = strlen("Device"); i < maxdevlen; i++)
+ msg_ginfo(" ");
+
+ msg_ginfo(" %s IDs Status\n", type);
+
+ for (i = 0; devs[i].vendor_name != NULL; i++) {
+ msg_ginfo("%s", devs[i].vendor_name);
+ unsigned int j;
+ for (j = strlen(devs[i].vendor_name); j < maxvendorlen; j++)
+ msg_ginfo(" ");
+ msg_ginfo("%s", devs[i].device_name);
+ for (j = strlen(devs[i].device_name); j < maxdevlen; j++)
+ msg_ginfo(" ");
+
+ msg_pinfo(" %04x:%04x %s\n", devs[i].vendor_id, devs[i].device_id,
+ test_state_to_text(devs[i].status));
+ }
+}
+
+int print_supported(void)
+{
+ unsigned int i;
+ if (print_supported_chips())
+ return 1;
+
+ msg_ginfo("\nSupported programmers:\n");
+ list_programmers_linebreak(0, 80, 0);
+ msg_ginfo("\n");
+#if CONFIG_INTERNAL == 1
+ msg_ginfo("\nSupported devices for the %s programmer:\n\n",
+ programmer_table[PROGRAMMER_INTERNAL].name);
+ print_supported_chipsets();
+ msg_ginfo("\n");
+ print_supported_boards_helper(boards_known, "mainboards");
+ msg_ginfo("\n");
+ print_supported_boards_helper(laptops_known, "mobile devices");
+#endif
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ const struct programmer_entry prog = programmer_table[i];
+ switch (prog.type) {
+ case USB:
+ print_supported_devs(prog, "USB");
+ break;
+ case PCI:
+ print_supported_devs(prog, "PCI");
+ break;
+ case OTHER:
+ if (prog.devs.note != NULL) {
+ msg_ginfo("\nSupported devices for the %s programmer:\n", prog.name);
+ msg_ginfo("%s", prog.devs.note);
+ }
+ break;
+ default:
+ msg_gerr("\n%s: %s: Uninitialized programmer type! Please report a bug at "
+ "flashrom@flashrom.org\n", __func__, prog.name);
+ break;
+ }
+ }
+ return 0;
+}
+
+#if CONFIG_INTERNAL == 1
+
+#ifdef CONFIG_PRINT_WIKI
+#define B(vendor, name, status, url, note) { vendor, name, status, url, note }
+#else
+#define B(vendor, name, status, url, note) { vendor, name, status }
+#endif
+
+/* Please keep this list alphabetically ordered by vendor/board. */
+const struct board_info boards_known[] = {
+#if defined(__i386__) || defined(__x86_64__)
+ B("A-Trend", "ATC-6220", OK, "http://www.motherboard.cz/mb/atrend/atc6220.htm", NULL),
+ B("abit", "A-S78H", OK, NULL, NULL),
+ B("abit", "AN-M2", OK, NULL, NULL),
+ B("abit", "AV8", OK, NULL, NULL),
+ B("abit", "AX8", OK, NULL, NULL),
+ B("abit", "BF6", OK, NULL, NULL),
+ B("abit", "BM6", OK, NULL, NULL),
+ B("abit", "BX6 2.0", OK, NULL, NULL),
+ B("abit", "Fatal1ty F-I90HD", OK, NULL, NULL),
+ B("abit", "IC7", OK, NULL, NULL),
+ B("abit", "IP35", OK, NULL, NULL),
+ B("abit", "IP35 Pro", OK, NULL, NULL),
+ B("abit", "IS-10", BAD, NULL, "Reported by deejkuba@aol.com to flashrom@coreboot.org, no public archive. Missing board enable and/or M50FW040 unlocking. May work now."),
+ B("abit", "KN8 Ultra", OK, NULL, NULL),
+ B("abit", "KN9 Ultra", OK, NULL, NULL),
+ B("abit", "NF-M2 nView", OK, NULL, NULL),
+ B("abit", "NF-M2S", OK, NULL, NULL),
+ B("abit", "NF7-S", OK, NULL, NULL),
+ B("abit", "VA6", OK, NULL, NULL),
+ B("abit", "VT6X4", OK, NULL, NULL),
+ B("Acer", "V75-M", OK, NULL, "This is an OEM board used by IBM in e.g. Aptiva 2170-G"),
+ B("Acer", "EM61SM/EM61PM", OK, NULL, "Used in Acer Aspire T180 and E380. Seems to be an OEM variant of abit's NF-M2S."),
+ B("Acorp", "6A815EPD", OK, "http://web.archive.org/web/20021206163652/www.acorp.com.tw/English/default.asp", NULL),
+ B("Acorp", "6M810C", OK, NULL, NULL),
+ B("ADLINK", "Express-HR", OK, "http://www.adlinktech.com/PD/web/PD_detail.php?pid=1012", NULL),
+ B("Advantech", "PCM-5820", OK, "http://www.emacinc.com/sbc_pc_compatible/pcm_5820.htm", NULL),
+ B("agami", "Aruma", OK, "http://web.archive.org/web/20080212111524/http://www.agami.com/site/ais-6000-series", NULL),
+ B("Albatron", "PM266A Pro", OK, "http://www.albatron.com.tw/English/Product/MB/pro_detail.asp?rlink=Overview&no=56", NULL), /* FIXME */
+ B("Alienware", "Aurora-R2", BAD, NULL, "Mainboard model is 0RV30W. Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("AOpen", "i945GMx-VFX", OK, NULL, "This is (also?) an OEM board from FSC (used in e.g. ESPRIMO Q5010 with designation D2544-B1)."),
+ B("AOpen", "UK79G-1394", OK, "http://global.aopen.com/products_detail.aspx?auno=9", "Used in EZ18 barebones"),
+ B("AOpen", "vKM400Am-S", OK, "http://global.aopen.com/products_detail.aspx?Auno=824", NULL),
+ B("Artec Group","DBE61", OK, "http://wiki.thincan.org/DBE61", NULL),
+ B("Artec Group","DBE62", OK, "http://wiki.thincan.org/DBE62", NULL),
+ B("ASI", "MB-5BLMP", OK, "http://www.hojerteknik.com/winnet.htm", "Used in the IGEL WinNET III thin client."),
+ B("ASRock", "4CoreDual-VSTA", OK, "http://www.asrock.com/mb/overview.asp?Model=4CoreDual-VSTA", "W39V040FB"),
+ B("ASRock", "775Dual-VSTA", OK, "http://www.asrock.com/mb/overview.asp?Model=775Dual-VSTA", NULL),
+ B("ASRock", "775i65G", OK, "http://www.asrock.com/mb/overview.asp?Model=775i65G", NULL),
+ B("ASRock", "880G Pro3", OK, "http://www.asrock.com/mb/overview.asp?Model=880G%20Pro3", NULL),
+ B("ASRock", "890GX Extreme3", OK, "http://www.asrock.com/mb/overview.asp?Model=890GX%20Extreme3", NULL),
+ B("ASRock", "939A785GMH/128M", OK, "http://www.asrock.com/mb/overview.asp?Model=939A785GMH/128M", NULL),
+ B("ASRock", "960GM-GS3 FX", OK, "http://www.asrock.com/mb/overview.asp?Model=960GM-GS3%20FX", NULL),
+ B("ASRock", "A330GC", OK, "http://www.asrock.com/mb/overview.asp?Model=A330GC", NULL),
+ B("ASRock", "A770CrossFire", OK, "http://www.asrock.com/mb/overview.asp?Model=A770CrossFire", NULL),
+ B("ASRock", "A780FullHD", OK, "http://www.asrock.com/mb/overview.asp?Model=A780FullHD", "While flashrom is working correctly, there might be problems with the firmware images themselves. Please see https://flashrom.org/pipermail/flashrom/2012-July/009600.html for details."),
+ B("ASRock", "ALiveNF6G-DVI", OK, "http://www.asrock.com/mb/overview.asp?Model=ALiveNF6G-DVI", NULL),
+ B("ASRock", "AM2NF6G-VSTA", OK, "http://www.asrock.com/mb/overview.asp?Model=AM2NF6G-VSTA", NULL),
+ B("ASRock", "AMCP7AION-HT", OK, "http://www.asrock.com/nettop/NVIDIA/ION%20330HT/", "Used in ION 330HT(-BD) barebones."),
+ B("ASRock", "ConRoeXFire-eSATA2", OK, "http://www.asrock.com/mb/overview.asp?model=conroexfire-esata2", NULL),
+ B("ASRock", "E350M1/USB3", OK, "http://www.asrock.com/mb/overview.asp?model=e350m1/usb3", "Vendor firmware writes to flash at shutdown. This probably corrupts the flash in case you write coreboot while running the vendor firmware. Simply updating the vendor firmware should be fine."),
+ B("ASRock", "Fatal1ty 970 Performance", OK, "http://www.asrock.com/mb/overview.asp?Model=Fatal1ty%20970%20Performance", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASRock", "Fatal1ty Z77 Performance", BAD, "http://www.asrock.com/mb/overview.asp?Model=Fatal1ty%20Z77%20Performance", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASRock", "G31M-GS", OK, "http://www.asrock.com/mb/overview.asp?Model=G31M-GS", NULL),
+ B("ASRock", "G31M-S rev 2.0", OK, "http://www.asrock.com/mb/overview.asp?model=G31M-S", NULL),
+ B("ASRock", "G41M-VS3", OK, "http://www.asrock.com/mb/overview.asp?Model=G41M-VS3", NULL),
+ B("ASRock", "H61M-ITX", BAD, "http://www.asrock.com/mb/overview.asp?Model=H61M-ITX", "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASRock", "H67M", BAD, "http://www.asrock.com/mb/overview.asp?Model=H67M", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASRock", "IMB-180-H", OK, "http://www.asrock.com/ipc/overview.asp?Model=IMB-A180-H", NULL),
+ B("ASRock", "K7S41", OK, "http://www.asrock.com/mb/overview.asp?Model=K7S41", NULL),
+ B("ASRock", "K7S41GX", OK, "http://www.asrock.com/mb/overview.asp?Model=K7S41GX", NULL),
+ B("ASRock", "K7VT4A+", BAD, "http://www.asrock.com/mb/overview.asp?Model=K7VT4A%2b", "No chip found, probably due to flash translation. https://flashrom.org/pipermail/flashrom/2009-August/000393.html"),
+ B("ASRock", "K8S8X", OK, "http://www.asrock.com/mb/overview.asp?Model=K8S8X", NULL),
+ B("ASRock", "M3A790GXH/128M", OK, "http://www.asrock.com/mb/overview.asp?Model=M3A790GXH/128M", NULL),
+ B("ASRock", "N61P-S", OK, "http://www.asrock.com/mb/overview.asp?Model=N61P-S", NULL),
+ B("ASRock", "N68C-S UCC", OK, "http://www.asrock.com/mb/overview.asp?Model=N68C-S%20UCC", NULL),
+ B("ASRock", "P4i65G", OK, "http://www.asrock.com/mb/overview.asp?Model=P4i65G", NULL),
+ B("ASRock", "P4i65GV", OK, "http://www.asrock.com/mb/overview.asp?Model=P4i65GV", NULL),
+ B("ASRock", "Z68 Extreme4", BAD, "http://www.asrock.com/mb/overview.asp?Model=Z68%20Extreme4", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "A7N8X Deluxe", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7N8X_Deluxe/", NULL),
+ B("ASUS", "A7N8X-E Deluxe", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7N8XE_Deluxe/", NULL),
+ B("ASUS", "A7N8X-VM/400", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7N8XVM400/", NULL),
+ B("ASUS", "A7V133", OK, NULL, NULL),
+ B("ASUS", "A7V333", OK, NULL, NULL),
+ B("ASUS", "A7V400-MX", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V400MX/", NULL),
+ B("ASUS", "A7V600-X", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V600X/", NULL),
+ B("ASUS", "A7V8X", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V8X/", NULL),
+ B("ASUS", "A7V8X-MX", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V8XMX/", NULL),
+ B("ASUS", "A7V8X-MX SE", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V8XMX_SE/", NULL),
+ B("ASUS", "A7V8X-X", OK, "http://www.asus.com/Motherboards/AMD_Socket_A/A7V8XX/", NULL),
+ B("ASUS", "A8M2N-LA (NodusM3-GL8E)", OK, "http://h10010.www1.hp.com/ewfrf/wc/document?docname=c00757531&cc=us&dlc=en&lc=en", "This is an OEM board from HP, the HP name is NodusM3-GL8E."),
+ B("ASUS", "A8N-E", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NE/", NULL),
+ B("ASUS", "A8N-LA (Nagami-GL8E)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?lc=en&cc=us&docname=c00647121&dlc=en", "This is an OEM board from HP, the HP name is Nagami-GL8E."),
+ B("ASUS", "A8N-SLI", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NSLI/", NULL),
+ B("ASUS", "A8N-SLI Deluxe", NT, NULL, "Should work out of the box since r1593."),
+ B("ASUS", "A8N-SLI Premium", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NSLI_Premium/", NULL),
+ B("ASUS", "A8N-VM", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NVM/", NULL),
+ B("ASUS", "A8N-VM CSM", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8NVM_CSM/", NULL),
+ B("ASUS", "A8NE-FM/S", OK, "http://www.hardwareschotte.de/hardware/preise/proid_1266090/preis_ASUS+A8NE-FM", NULL),
+ B("ASUS", "A8V Deluxe", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8V_Deluxe/", NULL),
+ B("ASUS", "A8V-E Deluxe", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8VE_Deluxe/", NULL),
+ B("ASUS", "A8V-E SE", OK, "http://www.asus.com/Motherboards/AMD_Socket_939/A8VE_SE/", "See http://www.coreboot.org/pipermail/coreboot/2007-October/026496.html"),
+ B("ASUS", "C60M1-I", OK, "http://www.asus.com/Motherboards/C60M1I/", "The MAC address of the onboard network card is stored in flash."),
+ B("ASUS", "Crosshair II Formula", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/Crosshair_II_Formula/", NULL),
+ B("ASUS", "Crosshair IV Extreme", OK, "http://www.asus.com/Motherboards/AMD_AM3/Crosshair_IV_Extreme/", NULL),
+ B("ASUS", "CUSL2-C", OK, NULL, "The image provided by ASUS is only 256 kB big and has to be written to the upper 256 kB of the 512 kB chip."),
+ B("ASUS", "DSAN-DX", NT, "http://www.asus.com/Server_Workstation/Server_Motherboards/DSANDX/", NULL),
+ B("ASUS", "E35M1-I DELUXE", OK, "http://www.asus.com/Motherboards/AMD_CPU_on_Board/E35M1I_DELUXE/", NULL),
+ B("ASUS", "F1A75-V PRO", OK, "http://www.asus.com/Motherboard/F1A75V_PRO/", NULL),
+ B("ASUS", "F2A85-M", DEP, "https://www.asus.com/Motherboards/F2A85M/", "UEFI builds v6404 and above disable access to some parts of the flash, cf. http://www.coreboot.org/ASUS_F2A85-M#UEFI_builds_that_allow_flash_chip_access"),
+ B("ASUS", "K8N", OK, "http://www.asus.com/Motherboards/AMD_Socket_754/K8N/", NULL),
+ B("ASUS", "K8V", OK, "http://www.asus.com/Motherboards/AMD_Socket_754/K8V/", NULL),
+ B("ASUS", "K8V SE Deluxe", OK, "http://www.asus.com/Motherboards/AMD_Socket_754/K8V_SE_Deluxe/", NULL),
+ B("ASUS", "K8V-X", OK, "http://www.asus.com/Motherboards/AMD_Socket_754/K8VX/", NULL),
+ B("ASUS", "K8V-X SE", OK, "http://www.asus.com/Motherboards/AMD_Socket_754/K8VX_SE/", NULL),
+ B("ASUS", "KFSN4-DRE/SAS", OK, "http://www.asus.com/Server_Workstation/Server_Motherboards/KFSN4DRESAS/", NULL),
+ B("ASUS", "M2A-MX", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2AMX/", NULL),
+ B("ASUS", "M2A-VM (HDMI)", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2AVM/", NULL),
+ B("ASUS", "M2N32-SLI Deluxe", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2N32SLI_DeluxeWireless_Edition/", NULL),
+ B("ASUS", "M2N68-VM", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M2N68VM/", NULL),
+ B("ASUS", "M2NBP-VM CSM", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2NBPVM_CSM/", NULL),
+ B("ASUS", "M2N-E", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2NE/", "If the machine doesn't come up again after flashing, try resetting the NVRAM(CMOS). The MAC address of the onboard network card will change to the value stored in the new image, so backup the old address first. See https://flashrom.org/pipermail/flashrom/2009-November/000879.html"),
+ B("ASUS", "M2N-E SLI", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2NE_SLI/", NULL),
+ B("ASUS", "M2N-MX SE Plus", OK, "http://www.asus.com/Motherboards/M2NMX_SE_Plus/", NULL),
+ B("ASUS", "M2NPV-VM", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2NPVVM/", NULL),
+ B("ASUS", "M2N-SLI Deluxe", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2NSLI_Deluxe/", NULL),
+ B("ASUS", "M2V", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2V/", NULL),
+ B("ASUS", "M2V-MX", OK, "http://www.asus.com/Motherboards/AMD_AM2/M2VMX/", NULL),
+ B("ASUS", "M3A", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3A/", NULL),
+ B("ASUS", "M3A76-CM", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3A76CM/", NULL),
+ B("ASUS", "M3A78-EH", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3A78EH/", NULL),
+ B("ASUS", "M3A78-EM", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3A78EM/", NULL),
+ B("ASUS", "M3N78 PRO", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3N78_PRO/", NULL),
+ B("ASUS", "M3N78-VM", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M3N78VM/", NULL),
+ B("ASUS", "M3N-H/HDMI", OK, "http://www.asus.com/Motherboards/M3NHHDMI//", NULL),
+ B("ASUS", "M4A785TD-M EVO", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A785TDM_EVO/", NULL),
+ B("ASUS", "M4A785TD-V EVO", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A785TDV_EVO/", NULL),
+ B("ASUS", "M4A785T-M", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A785TM/", NULL),
+ B("ASUS", "M4A78-EM", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M4A78EM/", NULL),
+ B("ASUS", "M4A78LT-M LE", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A78LTM_LE/", NULL),
+ B("ASUS", "M4A79T Deluxe", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A79T_Deluxe/", NULL),
+ B("ASUS", "M4A87TD/USB3", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A87TDUSB3/", NULL),
+ B("ASUS", "M4A89GTD PRO", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4A89GTD_PRO/", NULL),
+ B("ASUS", "M4N68T V2", OK, "http://www.asus.com/Motherboards/AMD_AM3/M4N68T_V2/", NULL),
+ B("ASUS", "M4N78 PRO", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M4N78_PRO/", NULL),
+ B("ASUS", "M4N78 SE", OK, "http://www.asus.com/Motherboards/AMD_AM2Plus/M4N78_SE/", NULL),
+ B("ASUS", "M5A78L-M LX", OK, "http://www.asus.com/Motherboards/AMD_AM3Plus/M5A78LM_LX/", "The MAC address of the onboard LAN NIC is stored in flash, hence overwritten by flashrom; see https://flashrom.org/pipermail/flashrom/2012-May/009200.html"),
+ B("ASUS", "M5A97 (rev. 1.0)", OK, "http://www.asus.com/Motherboard/M5A97/", NULL),
+ B("ASUS", "M5A99X EVO", OK, "http://www.asus.com/Motherboards/AMD_AM3Plus/M5A99X_EVO/", NULL),
+ B("ASUS", "Maximus IV Extreme", BAD, "http://www.asus.com/Motherboards/Intel_Socket_1155/Maximus_IV_Extreme/", "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "MEW-AM", BAD, NULL, "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. May work now."),
+ B("ASUS", "MEW-VM", BAD, "http://www.elhvb.com/mboards/OEM/HP/manual/ASUS%20MEW-VM.htm", "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. May work now."),
+ B("ASUS", "OPLX-M", NT, NULL, "Untested board enable."),
+ B("ASUS", "P2B", OK, NULL, NULL),
+ B("ASUS", "P2B-D", OK, NULL, NULL),
+ B("ASUS", "P2B-DS", OK, NULL, NULL),
+ B("ASUS", "P2B-F", OK, NULL, NULL),
+ B("ASUS", "P2B-LS", OK, NULL, NULL),
+ B("ASUS", "P2B-N", OK, NULL, NULL),
+ B("ASUS", "P2E-M", OK, NULL, NULL),
+ B("ASUS", "P2L97-S", OK, NULL, NULL),
+ B("ASUS", "P3B-F", BAD, NULL, "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. May work now."),
+ B("ASUS", "P4B266", OK, NULL, NULL),
+ B("ASUS", "P4B266-LM", OK, "http://esupport.sony.com/US/perl/swu-list.pl?mdl=PCVRX650", NULL),
+ B("ASUS", "P4B533-E", OK, NULL, NULL),
+ B("ASUS", "P4C800-E Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4C800E_Deluxe/", NULL),
+ B("ASUS", "P4GV-LA (Guppy)", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00363478", NULL),
+ B("ASUS", "P4P800", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4P800/", NULL),
+ B("ASUS", "P4P800-E Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4P800E_Deluxe/", NULL),
+ B("ASUS", "P4P800-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4P800VM/", NULL),
+ B("ASUS", "P4P800-X", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4P800X/", NULL),
+ B("ASUS", "P4PE-X/TE", NT, "http://www.asus.com/999/html/events/mb/socket478/p4pe-x-te/overview.htm", NULL),
+ B("ASUS", "P4S533-X", OK, NULL, NULL),
+ B("ASUS", "P4S800-MX", OK, "http://www.asus.com/Motherboards/Intel_Socket_478/P4S800MX/", NULL),
+ B("ASUS", "P4SC-E", OK, NULL, "Part of ASUS Terminator P4 533 barebone system"),
+ B("ASUS", "P4SD-LA", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00022505", NULL),
+ B("ASUS", "P5A", OK, NULL, NULL),
+ B("ASUS", "P5B", OK, NULL, NULL),
+ B("ASUS", "P5B-Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5B_Deluxe/", NULL),
+ B("ASUS", "P5B-VM", OK, NULL, NULL),
+ B("ASUS", "P5BV-M", BAD, NULL, "Reported by Bernhard M. Wiedemann <bernhard@uml12d.zq1.de> to flashrom@coreboot.org, no public archive. Missing board enable and/or SST49LF008A unlocking. May work now."),
+ B("ASUS", "P5BV-R", OK, "http://www.asus.com/Server_Workstation/Servers/RS120E5PA2/", "Used in RS120-E5/PA2 servers."),
+ B("ASUS", "P5GC-MX/1333", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GCMX1333/", NULL),
+ B("ASUS", "P5GD1 Pro", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GD1_PRO/", NULL),
+ B("ASUS", "P5GD1-VM/S", OK, NULL, "This is an OEM board from FSC. Although flashrom supports it and can probably not distinguish it from the P5GD1-VM, please note that the P5GD1-VM BIOS does not support the FSC variants completely."),
+ B("ASUS", "P5GD1(-VM)", NT, NULL, "Untested board enable."),
+ B("ASUS", "P5GD2 Premium", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GD2_Premium/", NULL),
+ B("ASUS", "P5GD2-X", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GD2X/", NULL),
+ B("ASUS", "P5GDC Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GDC_Deluxe/", NULL),
+ B("ASUS", "P5GDC-V Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5GDCV_Deluxe/", NULL),
+ B("ASUS", "P5GD2/C variants", NT, NULL, "Untested board enable."),
+ B("ASUS", "P5K SE", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5K_SE/", NULL),
+ B("ASUS", "P5K-V", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5KV/", NULL),
+ B("ASUS", "P5K-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5KVM/", NULL),
+ B("ASUS", "P5KC", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5KC/", NULL),
+ B("ASUS", "P5KPL-AM IN/GB", OK, "http://support.asus.com/download.aspx?SLanguage=en&m=P5KPL-AM+IN%2fGB&os=29", NULL),
+ B("ASUS", "P5KPL-CM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5KPLCM/", NULL),
+ B("ASUS", "P5KPL-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5KPLVM/", "Found in V3-P5G31."),
+ B("ASUS", "P5L-MX", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LMX/", NULL),
+ B("ASUS", "P5L-VM 1394", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LVM_1394/", NULL),
+ B("ASUS", "P5LD2", OK, NULL, NULL),
+ B("ASUS", "P5LD2-MQ", OK, "http://support.asus.com/download.aspx?SLanguage=en&p=8&s=12&m=Vintage-PH2&os=&hashedid=n/a", "Found in ASUS Vintage-PH2 barebones."),
+ B("ASUS", "P5LD2-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LD2VM/", NULL),
+ B("ASUS", "P5LD2-VM DH", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5LD2VM_DH/", NULL),
+ B("ASUS", "P5LP-LE (Lithium-UL8E)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00379616&tmp_task=prodinfoCategory&cc=us&dlc=en&lc=en&product=1159887", "This is an OEM board from HP."),
+ B("ASUS", "P5LP-LE (Epson OEM)", OK, NULL, "This is an OEM board from Epson (e.g. Endeavor MT7700)."),
+ B("ASUS", "P5LP-LE", NT, NULL, "This designation is used for OEM boards from HP, Epson and maybe others. The HP names vary and not all of them have been tested yet. Please report any success or failure, thanks."),
+ B("ASUS", "P5N-D", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5ND/", NULL),
+ B("ASUS", "P5N-E SLI", NT, "http://www.asus.com/Motherboards/Intel_Socket_775/P5NE_SLI/", "Untested board enable."),
+ B("ASUS", "P5N32-E SLI", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5N32E_SLI/", NULL),
+ B("ASUS", "P5N7A-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5N7AVM/", NULL),
+ B("ASUS", "P5ND2-SLI Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5ND2SLI_Deluxe/", NULL),
+ B("ASUS", "P5PE-VM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5PEVM/", NULL),
+ B("ASUS", "P5QPL-AM", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5QPLAM/", NULL),
+ B("ASUS", "P5VD1-X", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5VD1X/", NULL),
+ B("ASUS", "P5VD2-MX", OK, "http://www.asus.com/Motherboards/Intel_Socket_775/P5VD2MX/", "The MAC address of the onboard LAN NIC is stored in flash, hence overwritten by flashrom; see https://flashrom.org/pipermail/flashrom/2012-March/009014.html"),
+ B("ASUS", "P6T SE", OK, "http://www.asus.com/Motherboards/Intel_Socket_1366/P6T_SE/", NULL),
+ B("ASUS", "P6T Deluxe", OK, "http://www.asus.com/Motherboards/Intel_Socket_1366/P6T_Deluxe/", NULL),
+ B("ASUS", "P6T Deluxe V2", OK, "http://www.asus.com/Motherboards/Intel_Socket_1366/P6T_Deluxe_V2/", NULL),
+ B("ASUS", "P7H57D-V EVO", OK, "http://www.asus.com/Motherboards/Intel_Socket_1156/P7H57DV_EVO/", NULL),
+ B("ASUS", "P7H55-M LX", BAD, NULL, "flashrom works correctly, but GbE LAN is nonworking (probably due to a missing/bogus MAC address; see https://flashrom.org/pipermail/flashrom/2011-July/007432.html and http://ubuntuforums.org/showthread.php?t=1534389 for a possible workaround)"),
+ B("ASUS", "P8B-E/4L", BAD, NULL, "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8B WS", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8B75-M LE", BAD, NULL, "Probing works (2x 8192 kB via hwseq), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8H61 PRO", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8H61-M LE/USB3", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8H67-M PRO", BAD, NULL, "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."), // some firmware versions apparently are not locked, see report by Marek Zakrzewski
+ B("ASUS", "P8H77-I", OK, "http://www.asus.com/Motherboards/P8H77I/", NULL),
+ B("ASUS", "P8H77-M", OK, "http://www.asus.com/Motherboards/P8H77M/", NULL),
+ B("ASUS", "P8H77-V LE", OK, "http://www.asus.com/Motherboards/P8H77V_LE/", NULL),
+ B("ASUS", "P8P67 (rev. 3.1)", BAD, NULL, "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8P67 LE (B2)", OK, NULL, NULL),
+ B("ASUS", "P8P67 LE (B3)", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8P67 PRO (rev. 3.0)", OK, "http://www.asus.com/Motherboards/Intel_Socket_1155/P8P67_PRO/", NULL),
+ B("ASUS", "P8P67-M PRO", BAD, NULL, "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8Z68-V", OK, "http://www.asus.com/Motherboards/Intel_Socket_1155/P8Z68V/", "Warning: MAC address of LOM is stored at 0x1000 - 0x1005 of the image."),
+ B("ASUS", "P8Z68-V LE", BAD, NULL, "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8Z68-V PRO", BAD, NULL, "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ASUS", "P8Z68-V PRO/GEN3", OK, "http://www.asus.com/Motherboards/Intel_Socket_1155/P8Z68V_PROGEN3/", "Warning: MAC address of LOM is stored at 0x1000 - 0x1005 of the image."),
+ B("ASUS", "RAMPAGE III GENE", OK, "http://www.asus.com/Motherboards/RAMPAGE_III_GENE/", "The MAC address of the onboard network card is stored in flash."),
+ B("ASUS", "SABERTOOTH 990FX", OK, "http://www.asus.com/Motherboards/AMD_AM3Plus/SABERTOOTH_990FX/", NULL),
+ B("ASUS", "SABERTOOTH 990FX R2.0", OK, "http://www.asus.com/Motherboards/AMD_AM3Plus/SABERTOOTH_990FX_R20/", NULL),
+ B("ASUS", "TUSL2-C", NT, "http://support.asus.com/download.aspx?SLanguage=en&p=1&s=4&m=TUSL2-C&os=&hashedid=n/a", "Untested board enable."),
+ B("ASUS", "Z8NA-D6C", OK, "http://www.asus.com/Server_Workstation/Server_Motherboards/Z8NAD6C/", NULL),
+ B("ASUS", "Z8PE-D12", OK, "http://www.asus.com/Server_Workstation/Server_Motherboards/Z8PED12/", NULL),
+ B("Attro", "G5G100-P", OK, "http://www.attro.com/motherboard/G5G100-P.htm", NULL),
+ B("Bachmann", "OT200", OK, "http://www.bachmann.info/produkte/bedien-und-beobachtungsgeraete/operator-terminals/", NULL),
+ B("BCOM", "WinNET100", OK, "http://www.coreboot.org/BCOM_WINNET100", "Used in the IGEL-316 thin client."),
+ B("Bifferos", "Bifferboard", OK, "http://bifferos.co.uk/", NULL),
+ B("Biostar", "H61MGC", BAD, NULL, "Probing works (Eon EN25Q32(A/B), 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Biostar", "H61MU3", BAD, NULL, "Probing works (Eon EN25Q32(A/B), 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Biostar", "M6TBA", BAD, "ftp://ftp.biostar-usa.com/manuals/M6TBA/", "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. May work now."),
+ B("Biostar", "M7NCD Pro", OK, "http://www.biostar.com.tw/app/en/mb/introduction.php?S_ID=260", NULL),
+ B("Biostar", "M7VIQ", NT, NULL, NULL),
+ B("Biostar", "N61PB-M2S", OK, NULL, NULL),
+ B("Biostar", "N68S3+", OK, NULL, NULL),
+ B("Biostar", "P4M80-M4", OK, "http://www.biostar-usa.com/mbdetails.asp?model=p4m80-m4", NULL),
+ B("Biostar", "TA780G M2+", OK, "http://www.biostar.com.tw/app/en/mb/introduction.php?S_ID=344", NULL),
+ B("Biostar", "TA790GX A3+", OK, "http://www.biostar.com.tw/app/en/mb/introduction.php?S_ID=395", NULL),
+ B("Boser", "HS-6637", BAD, "http://www.boser.com.tw/manual/HS-62376637v3.4.pdf", "Reported by Mark Robinson <mark@zl2tod.net> to flashrom@coreboot.org, no public archive. Missing board enable and/or F29C51002T unlocking. May work now."),
+ B("Congatec", "conga-X852", OK, "http://www.congatec.com/single_news+M57715f6263d.html?&L=1", NULL),
+ B("Dell", "Inspiron 580", BAD, "http://support.dell.com/support/edocs/systems/insp580/en/index.htm", "Probing works (Macronix MX25L6405, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("Dell", "OptiPlex 7010", BAD, NULL, "Mainboard model is 0KRC95. Probing works (Hardware Sequencing 4 + 8MB), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("Dell", "OptiPlex GX1", OK, "http://support.dell.com/support/edocs/systems/ban_gx1/en/index.htm", NULL),
+ B("Dell", "PowerEdge 1850", OK, "http://support.dell.com/support/edocs/systems/pe1850/en/index.htm", NULL),
+ B("Dell", "PowerEdge C6220", BAD, NULL, "Mainboard model is 0HYFFG. Probing works (Macronix MX25L6405, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked (and there are even overlapping PRs)."),
+ B("Dell", "Vostro 460", BAD, "http://support.dell.com/support/edocs/systems/vos460/en/index.htm", "Mainboard model is 0Y2MRG. Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("DFI", "855GME-MGF", BAD, "http://www.dfi.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?action=e&downloadType=&windowstate=normal&mode=view&downloadFlag=false&itemId=433", "Probably needs a board enable. http://www.coreboot.org/pipermail/coreboot/2009-May/048549.html"),
+ B("DFI", "AD77", NT, NULL, "Untested board enable."),
+ B("DFI", "Blood-Iron P35 T2RL", OK, "http://lp.lanparty.com.tw/portal/CM/cmproduct/XX_cmproddetail/XX_WbProdsWindow?itemId=516&downloadFlag=false&action=1", NULL),
+ B("Elitegroup", "848P-A7", OK, NULL, NULL),
+ B("Elitegroup", "GeForce6100PM-M2 (V3.0)", OK, NULL, NULL),
+ B("Elitegroup", "GeForce6100SM-M", OK, NULL, NULL),
+ B("Elitegroup", "GeForce7050M-M (V2.0)", OK, "http://www.ecs.com.tw/ECSWebSite/Product/Product_Detail.aspx?DetailID=865&MenuID=20&LanID=0", NULL),
+ B("Elitegroup", "GF7050VT-M", OK, NULL, NULL),
+ B("Elitegroup", "GF7100PVT-M3 (V1.0)", OK, NULL, NULL),
+ B("Elitegroup", "GF8200A", OK, NULL, NULL),
+ B("Elitegroup", "K7S5A", OK, NULL, NULL),
+ B("Elitegroup", "K7S6A", OK, NULL, NULL),
+ B("Elitegroup", "K7SEM (V1.0A)", OK, NULL, NULL),
+ B("Elitegroup", "K7VTA3", OK, NULL, NULL),
+ B("Elitegroup", "P4M800PRO-M (V1.0A, V2.0)", OK, NULL, NULL),
+ B("Elitegroup", "P4VXMS (V1.0A)", OK, NULL, NULL),
+ B("Elitegroup", "P6BAP-A+ (V2.2)", OK, NULL, NULL),
+ B("Elitegroup", "P6IWP-Fe", OK, NULL, NULL),
+ B("Elitegroup", "P6VAP-A+", OK, NULL, NULL),
+ B("Elitegroup", "RS485M-M", OK, NULL, NULL),
+ B("Emerson", "ATCA-7360", OK, "http://www.emerson.com/sites/Network_Power/en-US/Products/Product_Detail/Product1/Pages/EmbCompATCA-7360.aspx", NULL),
+ B("EPoX", "EP-3PTA", BAD, NULL, "Missing board enable (W83627HF/F/HG/G), see https://flashrom.org/pipermail/flashrom/2012-April/009043.html"),
+ B("EPoX", "EP-8K5A2", OK, "http://www.epox.com/product.asp?ID=EP-8K5A2", NULL),
+ B("EPoX", "EP-8NPA7I", OK, "http://www.epox.com/product.asp?ID=EP-8NPA7I", NULL),
+ B("EPoX", "EP-8RDA3+", OK, "http://www.epox.com/product.asp?ID=EP-8RDA3plus", NULL),
+ B("EPoX", "EP-9NPA7I", OK, "http://www.epox.com/product.asp?ID=EP-9NPA7I", NULL),
+ B("EPoX", "EP-BX3", OK, "http://www.epox.com/product.asp?ID=EP-BX3", NULL),
+ B("EVGA", "122-CK-NF68", OK, NULL, NULL),
+ B("EVGA", "132-CK-NF78", OK, "http://www.evga.com/articles/385.asp", NULL),
+ B("EVGA", "270-WS-W555-A2 (Classified SR-2)", OK, "http://www.evga.com/products/moreInfo.asp?pn=270-WS-W555-A2", NULL),
+ B("FIC", "VA-502", BAD, "ftp://ftp.fic.com.tw/motherboard/manual/socket7/va-502/", "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. Seems the PCI subsystem IDs are identical with the Tekram P6Pro-A5. May work now."),
+ B("Foxconn", "6150K8MD-8EKRSH", OK, "http://www.foxconnchannel.com/ProductDetail.aspx?T=Motherboard&U=en-us0000157", NULL),
+ B("Foxconn", "A6VMX", OK, "http://www.foxconnchannel.com/ProductDetail.aspx?T=Motherboard&U=en-us0000346", NULL),
+ B("Foxconn", "P4M800P7MA-RS2", OK, "http://www.foxconnchannel.com/ProductDetail.aspx?T=Motherboard&U=en-us0000138", NULL),
+ B("Foxconn", "P55MX", OK, "http://www.foxconnchannel.com/ProductDetail.aspx?T=motherboard&U=en-us0000474", "Needs the MFG jumper to be set correctly before flashing to enable the Flash Descriptor Override Strap."),
+ B("Foxconn", "Q45M", BAD, "http://www.foxconnchannel.com/ProductDetail.aspx?T=Motherboard&U=en-us0000587", "Probing works (Hardware sequencing, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("Freetech", "P6F91i", OK, "http://web.archive.org/web/20010417035034/http://www.freetech.com/prod/P6F91i.html", NULL),
+ B("Fujitsu", "D2724-A1x", OK, NULL, "Used in ESPRIMO E5625."),
+ B("Fujitsu", "D3041-A1x", OK, NULL, "Used in ESPRIMO P2560, contains an Atmel AT26DF081A."),
+ B("Fujitsu-Siemens", "CELSIUS W410", BAD, "ftp://ftp.ts.fujitsu.com/pub/mainboard-oem-sales/Products/Mainboards/Industrial&ExtendedLifetime/D3061&D3062/", "Mainboard model is D3062-A1. Probing works (Macronix MX25L6405, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("Fujitsu-Siemens", "ESPRIMO P5915", OK, "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/professionalpc/ESPRIMO/P/EsprimoP5915-6.htm", "Mainboard model is D2312-A2."),
+ B("GIGABYTE", "GA-2761GXDK", OK, "http://www.computerbase.de/news/hardware/mainboards/amd-systeme/2007/mai/gigabyte_dtx-mainboard/", NULL),
+ B("GIGABYTE", "GA-6BXC", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1445", NULL),
+ B("GIGABYTE", "GA-6BXDU", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1429", NULL),
+ B("GIGABYTE", "GA-6IEM", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1379", NULL),
+ B("GIGABYTE", "GA-6VXE7+", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2410", NULL),
+ B("GIGABYTE", "GA-6ZMA", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1541", NULL),
+ B("GIGABYTE", "GA-770TA-UD3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3272", NULL),
+ B("GIGABYTE", "GA-7DXR", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1302", NULL),
+ B("GIGABYTE", "GA-7VT600", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1666", NULL),
+ B("GIGABYTE", "GA-7ZM", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1366", "Works fine if you remove jumper JP9 on the board and disable the flash protection BIOS option."),
+ B("GIGABYTE", "GA-880GMA-USB3 (rev. 3.1)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3817", NULL),
+ B("GIGABYTE", "GA-8I945GZME-RH", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2304", NULL),
+ B("GIGABYTE", "GA-8IP775", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1830", NULL),
+ B("GIGABYTE", "GA-8IRML", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1343", NULL),
+ B("GIGABYTE", "GA-8PE667 Ultra 2", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1607", NULL),
+ B("GIGABYTE", "GA-8S648", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1674", NULL),
+ B("GIGABYTE", "GA-8SIMLFS 2.0", OK, NULL, "This is an OEM board used by Fujitsu."),
+ B("GIGABYTE", "GA-8SIMLH", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1399", NULL),
+ B("GIGABYTE", "GA-945GCM-S2 (rev. 3.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2466", NULL),
+ B("GIGABYTE", "GA-945GM-S2", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2331", NULL),
+ B("GIGABYTE", "GA-945PL-S3P (rev. 6.6)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2541", NULL),
+ B("GIGABYTE", "GA-965GM-S2 (rev. 2.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2617", NULL),
+ B("GIGABYTE", "GA-965P-DS4", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2288", NULL),
+ B("GIGABYTE", "GA-965P-S3 (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2321", NULL),
+ B("GIGABYTE", "GA-970A-D3P (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4642", NULL),
+ B("GIGABYTE", "GA-970A-UD3P (rev. 2.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=5194", "Primary flash chip is a Macronix MX25L3206E."),
+ B("GIGABYTE", "GA-990FXA-UD3 (rev. 4.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4672", NULL),
+ B("GIGABYTE", "GA-A75M-UD2H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3928", NULL),
+ B("GIGABYTE", "GA-B85M-D3H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4567", NULL),
+ B("GIGABYTE", "GA-EG43M-S2H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2878", NULL),
+ B("GIGABYTE", "GA-EP31-DS3L (rev. 1.0, 2.1)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2964", NULL),
+ B("GIGABYTE", "GA-EP35-DS3L", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2778", NULL),
+ B("GIGABYTE", "GA-EX58-UD4P", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2986", NULL),
+ B("GIGABYTE", "GA-G33M-S2", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2557", NULL),
+ B("GIGABYTE", "GA-G33M-S2L", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2692", NULL),
+ B("GIGABYTE", "GA-G41MT-S2PT", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3960", NULL),
+ B("GIGABYTE", "GA-H55M-S2", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3509", "8 MB (ME) + 1 MB (BIOS) flash chips - hardware sequencing required."),
+ B("GIGABYTE", "GA-H61M-D2-B3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3773", NULL),
+ B("GIGABYTE", "GA-H61M-D2H-USB3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4004", NULL),
+ B("GIGABYTE", "GA-H77-D3H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4141", "Does only work with -p internal:ich_spi_mode=hwseq due to an evil twin of MX25L6405 and ICH SPI lockdown."),
+ B("GIGABYTE", "GA-H77-DS3H (rev. 1.1)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4318", NULL),
+ B("GIGABYTE", "GA-H77M-D3H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4388", NULL),
+ B("GIGABYTE", "GA-J1900N-D3V", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4918", NULL),
+ B("GIGABYTE", "GA-K8N51GMF-9", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1939", NULL),
+ B("GIGABYTE", "GA-K8N51GMF", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1950", NULL),
+ B("GIGABYTE", "GA-K8N-SLI", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1928", NULL),
+ B("GIGABYTE", "GA-K8NS", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=1784", NULL),
+ B("GIGABYTE", "GA-M56S-S3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2607", NULL),
+ B("GIGABYTE", "GA-M57SLI-S4", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2287", NULL),
+ B("GIGABYTE", "GA-M61P-S3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2434", NULL),
+ B("GIGABYTE", "GA-M720-US3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3006", NULL),
+ B("GIGABYTE", "GA-MA69VM-S2", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2500", NULL),
+ B("GIGABYTE", "GA-MA74GM-S2H (rev. 3.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3152", NULL),
+ B("GIGABYTE", "GA-MA770-UD3 (rev. 2.1)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3302", NULL),
+ B("GIGABYTE", "GA-MA770T-UD3P", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3096", NULL),
+ B("GIGABYTE", "GA-MA780G-UD3H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3004", NULL),
+ B("GIGABYTE", "GA-MA785GMT-UD2H (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3156", NULL),
+ B("GIGABYTE", "GA-MA78G-DS3H (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2800", NULL),
+ B("GIGABYTE", "GA-MA78GM-S2H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2758", NULL), /* TODO: Rev. 1.BAD, 1.OK, or 2.x? */
+ B("GIGABYTE", "GA-MA78GPM-DS2H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2859", NULL),
+ B("GIGABYTE", "GA-MA790FX-DQ6", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2690", NULL),
+ B("GIGABYTE", "GA-MA790GP-DS4H", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2887", NULL),
+ B("GIGABYTE", "GA-MA790XT-UD4P (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3010", NULL),
+ B("GIGABYTE", "GA-P31-DS3L", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2615", NULL),
+ B("GIGABYTE", "GA-P31-S3G", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=2676", NULL),
+ B("GIGABYTE", "GA-P55-USB3 (rev. 2.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3440", NULL),
+ B("GIGABYTE", "GA-P55A-UD4 (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3436", NULL),
+ B("GIGABYTE", "GA-P55A-UD7" , OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3324", NULL),
+ B("GIGABYTE", "GA-P67A-UD3P", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3649", NULL),
+ B("GIGABYTE", "GA-X58A-UD3R (rev. 2.0)", OK, NULL, NULL),
+ B("GIGABYTE", "GA-X58A-UD7 (rev. 2.0)", OK, NULL, NULL),
+ B("GIGABYTE", "GA-X79-UD5", OK, NULL, NULL),
+ B("GIGABYTE", "GA-X79-UD3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4050", "Contains a Macronix MX25L6406E."),
+ B("GIGABYTE", "GA-X79-UP4 (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4288", NULL),
+ B("GIGABYTE", "GA-Z68MA-D2H-B3 (rev. 1.3)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3975", NULL),
+ B("GIGABYTE", "GA-Z68MX-UD2H-B (rev. 1.3)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3854", NULL),
+ B("GIGABYTE", "GA-Z68XP-UD3 (rev. 1.0)", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=3892", NULL),
+ B("GIGABYTE", "GA-Z77MX-D3H", BAD, "http://www.gigabyte.com/products/product-page.aspx?pid=4145", "Uses MX25L6436E and requires a small patch (but works flawlessly with that)."),
+ B("GIGABYTE", "GA-Z87-HD3", OK, "http://www.gigabyte.com/products/product-page.aspx?pid=4489", NULL),
+ B("HP", "8100 Elite CMT PC (304Bh)", BAD, NULL, "SPI lock down, PR, read-only descriptor, locked ME region."),
+ B("HP", "e-Vectra P2706T", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?lang=en&cc=us&prodSeriesId=77515&prodTypeId=12454", NULL),
+ B("HP", "Evans-GL6 (Pegatron IPMEL-AE)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?cc=us&lc=en&dlc=en&docname=c01925513", "Found in HP Pavilion Slimline s5220f."),
+ B("HP", "ProLiant DL145 G3", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00816835&lang=en&cc=us&taskId=101&prodSeriesId=3219755&prodTypeId=15351", NULL),
+ B("HP", "ProLiant DL165 G6", OK, "http://h10010.www1.hp.com/wwpc/us/en/sm/WF05a/15351-15351-3328412-241644-3328421-3955644.html", NULL),
+ B("HP", "ProLiant N40L", OK, NULL, NULL),
+ B("HP", "Puffer2-UL8E", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c00300023", NULL),
+ B("HP", "dc7800", BAD, "http://h10010.www1.hp.com/wwpc/us/en/sm/WF06a/12454-12454-64287-321860-3328898-3459241.html?dnr=1", "ICH9DO with SPI lock down, BIOS lock, PR, read-only descriptor, locked ME region."),
+ B("HP", "Vectra VL400", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060658&lang=en&cc=us", NULL),
+ B("HP", "Vectra VL420 SFF", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00060661&lang=en&cc=us", NULL),
+ B("HP", "xw4400 (0A68h)", BAD, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c00775230", "ICH7 with SPI lock down, BIOS lock, flash block detection (SST25VF080B); see http://paste.flashrom.org/view.php?id=686"),
+ B("HP", "xw6400", BAD, NULL, "No chip found, see https://flashrom.org/pipermail/flashrom/2012-March/009006.html"),
+ B("HP", "xw9300", BAD, "http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?lang=en&cc=us&prodTypeId=12454&prodSeriesId=459226", "Missing board enable, see https://flashrom.org/pipermail/flashrom/2012-March/008885.html"),
+ B("HP", "xw9400", OK, "http://h20000.www2.hp.com/bizsupport/TechSupport/Home.jsp?lang=en&cc=us&prodSeriesId=3211286&prodTypeId=12454", "Boot block is write protected unless the solder points next to F2 are shorted."),
+ B("HP", "Z400 Workstation (0AE4h)", BAD, NULL, "ICH10R with BIOS lock enable and a protected range PRBAD, see https://flashrom.org/pipermail/flashrom/2012-June/009350.html"),
+ B("IBASE", "MB899", OK, "http://www.ibase-i.com.tw/2009/mb899.html", NULL),
+ B("IBM", "x3455", OK, "http://www-03.ibm.com/systems/x/hardware/rack/x3455/index.html", NULL),
+ B("IEI", "PICOe-9452", OK, "http://www.ieiworld.com/product_groups/industrial/content.aspx?keyword=WSB&gid=00001000010000000001&cid=08125380291060861658&id=08142308605814597144", NULL),
+ B("Intel", "D201GLY", OK, "http://www.intel.com/support/motherboards/desktop/d201gly/index.htm", NULL),
+ B("Intel", "D2700MUD", BAD, "http://www.intel.com/cd/products/services/emea/eng/motherboards/desktop/D2700MUD/", "SMM protection enabled"),
+ B("Intel", "D425KT", BAD, "http://www.intel.com/content/www/us/en/motherboards/desktop-motherboards/desktop-board-d425kt.html", "NM10 with SPI lock down, BIOS lock, see https://flashrom.org/pipermail/flashrom/2012-January/008600.html"),
+ B("Intel", "D865GLC", BAD, NULL, "ICH5 with BIOS lock enable, see http://paste.flashrom.org/view.php?id=775"),
+ B("Intel", "D945GCNL", OK, NULL, NULL),
+ B("Intel", "DG45ID", BAD, "http://www.intel.com/products/desktop/motherboards/dg45id/dg45id-overview.htm", "Probing works (Winbond W25x32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked."),
+ B("Intel", "DQ965GF", BAD, NULL, "Probing enables Hardware Sequencing (behind that hides a SST SST25VF016B, 2048 kB). Parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked (and the platform data region seems to be bogus)."),
+ B("Intel", "DG965OT", BAD, NULL, "Probing enables Hardware Sequencing (behind that hides a SST SST25VF080B, 1024 kB). Parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME is locked (and the platform data region seems to be bogus)."),
+ B("Intel", "DH61AG ", BAD, NULL, "H61 with BIOS lock enable and locked ME region, see https://flashrom.org/pipermail/flashrom/2012-June/009417.html"),
+ B("Intel", "DH67CF", BAD, NULL, "H67 with BIOS lock enable and locked ME region, see https://flashrom.org/pipermail/flashrom/2011-September/007789.html"),
+ B("Intel", "DH67CL", BAD, NULL, "H67 with BIOS lock enable and locked ME region, see https://flashrom.org/pipermail/flashrom/2012-November/010112.html"),
+ B("Intel", "DN2800MT (Marshalltown)", BAD, NULL, "BIOS locked via BIOS_CNTL."),
+ B("Intel", "DQ45CB", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Intel", "DQ77MK", BAD, NULL, "Q77 with BIOS lock enable and locked ME region, see http://paste.flashrom.org/view.php?id=1603"),
+ B("Intel", "EP80759", OK, NULL, NULL),
+ B("Intel", "Foxhollow", OK, NULL, "Intel reference board."),
+ B("Intel", "Greencity", OK, NULL, "Intel reference board."),
+ B("Intel", "SE440BX-2", BAD, "http://downloadcenter.intel.com/SearchResult.aspx?lang=eng&ProductFamily=Desktop+Boards&ProductLine=Discontinued+Motherboards&ProductProduct=Intel%C2%AE+SE440BX-2+Motherboard", "Probably won't work, see http://www.coreboot.org/pipermail/flashrom/2010-July/003952.html"),
+ B("IWILL", "DK8-HTX", OK, "http://web.archive.org/web/20060507170150/http://www.iwill.net/product_2.asp?p_id=98", NULL),
+ B("Jetway", "J-7BXAN", OK, "http://www.jetway.com.tw/evisn/download/d7BXAS.htm", NULL),
+ B("Jetway", "J7F4K1G5D-PB", OK, "http://www.jetway.com.tw/jw/ipcboard_view.asp?productid=282&proname=J7F4K1G5D", NULL),
+ B("Kontron", "986LCD-M", OK, "http://de.kontron.com/products/boards+and+mezzanines/embedded+motherboards/miniitx+motherboards/986lcdmmitx.html", NULL),
+ B("Lanner", "EM-8510C", OK, NULL, NULL),
+ B("Lenovo", "Tilapia CRB", OK, NULL, "Used in ThinkCentre M75e."),
+ B("Lex", "CV700A", OK, "http://www.lex.com.tw/product/CV700A-spec.htm", NULL),
+ B("Mitac", "6513WU", OK, "http://web.archive.org/web/20050313054828/http://www.mitac.com/micweb/products/tyan/6513wu/6513wu.htm", NULL),
+ B("MSC", "Q7-TCTC", OK, "http://www.msc-ge.com/en/produkte/com/moduls/overview/5779-www.html", NULL),
+ B("MSI", "MS-6153", OK, "http://www.msi.com/product/mb/MS-6153.html", NULL),
+ B("MSI", "MS-6156", OK, "http://uk.ts.fujitsu.com/rl/servicesupport/techsupport/boards/Motherboards/MicroStar/Ms6156/MS6156.htm", NULL),
+ B("MSI", "MS-6163 (MS-6163 Pro)",OK, "http://www.msi.com/product/mb/MS-6163-Pro.html", NULL),
+ B("MSI", "MS-6178", BAD, "http://www.msi.com/product/mb/MS-6178.html", "Immediately powers off if you try to hot-plug the chip. However, this does '''not''' happen if you use coreboot. Owned by Uwe Hermann <uwe@hermann-uwe.de>."),
+ B("MSI", "MS-6330 (K7T Turbo)", OK, "http://www.msi.com/product/mb/K7T-Turbo.html", NULL),
+ B("MSI", "MS-6391 (845 Pro4)", OK, "http://www.msi.com/product/mb/845-Pro4.html", NULL),
+ B("MSI", "MS-6561 (745 Ultra)", OK, "http://www.msi.com/product/mb/745-Ultra.html", NULL),
+ B("MSI", "MS-6566 (845 Ultra-C)",OK, "http://www.msi.com/product/mb/845-Ultra-C.html", NULL),
+ B("MSI", "MS-6570 (K7N2)", OK, "http://www.msi.com/product/mb/K7N2.html", NULL),
+ B("MSI", "MS-6577 (Xenon)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?product=90390&lc=en&cc=us&dlc=en&docname=bph07843", "This is an OEM board from HP, the HP name is Xenon."),
+ B("MSI", "MS-6590 (KT4 Ultra)", OK, "http://www.msi.com/product/mb/KT4-Ultra.html", NULL),
+ //B("MSI", "MS-6702E (K8T Neo2-F/FIR)",OK, "http://www.msi.com/product/mb/K8T-Neo2-F--FIR.html", NULL), This was wrongly attributed to the MS-7094 board enable.
+ B("MSI", "MS-6704 (845PE Max2 PCB 1.0)", OK, "http://www.msi.com/product/mb/845PE-Max2.html", "Write protection must be disabled in the BIOS setup."),
+ B("MSI", "MS-6712 (KT4V)", OK, "http://www.msi.com/product/mb/KT4V---KT4V-L--v1-0-.html", NULL),
+ B("MSI", "MS-6787 (P4MAM-V/P4MAM-L)", OK, "http://www.msi.com/service/search/?kw=6787&type=product", NULL),
+ B("MSI", "MS-7005 (651M-L)", OK, "http://www.msi.com/product/mb/651M-L.html", NULL),
+ B("MSI", "MS-7025 (K8N Neo2 Platinum)", OK, "http://www.msi.com/product/mb/K8N-Neo2-Platinum.html", NULL),
+ B("MSI", "MS-7030 (K8N Neo Platinum)", OK, "http://www.msi.com/product/mb/K8N-Neo-Platinum.html", NULL),
+ B("MSI", "MS-7046", OK, "http://www.heimir.de/ms7046/", NULL),
+ B("MSI", "MS-7061 (KM4M-V/KM4AM-V)", OK, "http://www.msi.com/service/search/?kw=7061&type=product", NULL),
+ B("MSI", "MS-7065", OK, "http://browse.geekbench.ca/geekbench2/view/53114", NULL),
+ B("MSI", "MS-7094 (K8T Neo2-F V2.0)",OK, "http://www.msi.com/product/mb/K8T_Neo2F_V2.0.html", NULL),
+ B("MSI", "MS-7125 (K8N Neo4(-F/-FI/-FX/Platinum))", OK, "http://www.msi.com/product/mb/K8N_Neo4_Platinum_PCB_1.0.html", NULL),
+ B("MSI", "MS-7135 (K8N Neo3)", OK, "http://www.msi.com/product/mb/K8N-Neo3.html", NULL),
+ B("MSI", "MS-7142 (K8MM-V)", OK, "http://www.msi.com/product/mb/K8MM-V.html", NULL),
+ B("MSI", "MS-7168 (Orion)", OK, "http://support.packardbell.co.uk/uk/item/index.php?i=spec_orion&pi=platform_honeymoon_istart", NULL),
+ B("MSI", "MS-7207 (K8NGM2-L)", OK, "http://www.msi.com/product/mb/K8NGM2-FID--IL--L.html", NULL),
+ B("MSI", "MS-7211 (PM8M3-V)", OK, "http://www.msi.com/product/mb/PM8M3-V.html", NULL),
+ B("MSI", "MS-7236 (945PL Neo3)", OK, "http://www.msi.com/product/mb/945PL-Neo3.html", NULL),
+ B("MSI", "MS-7250 (K9N SLI (rev 2.1))", OK, "http://www.msi.com/product/mb/K9N--SLI.html", NULL),
+ B("MSI", "MS-7253 (K9VGM-V)", OK, "http://www.msi.com/product/mb/K9VGM-V.html", NULL),
+ B("MSI", "MS-7255 (P4M890M)", OK, "http://www.msi.com/product/mb/P4M890M-L-IL.html", NULL),
+ B("MSI", "MS-7260 (K9N Neo PCB 1.0)", BAD, "http://www.msi.com/product/mb/K9N-Neo--PCB-1-0-.html", "Interestingly flashrom does not work when the vendor BIOS is booted, but it ''does'' work flawlessly when the machine is booted with coreboot. Owned by Uwe Hermann <uwe@hermann-uwe.de>."),
+ B("MSI", "MS-7309 (K9N6SGM-V)", BAD, "http://www.msi.com/product/mb/K9N6SGM-V---K9N6PGM-FI---K9N6PGM-F.html", "Uses Fintek F71882F/F71883F/F71887 SPI-to-LPC translation."),
+ B("MSI", "MS-7309 (K9N6PGM2-V2)", OK, "http://www.msi.com/product/mb/K9N6PGM2-V2.html", NULL),
+ B("MSI", "MS-7312 (K9MM-V)", OK, "http://www.msi.com/product/mb/K9MM-V.html", NULL),
+ B("MSI", "MS-7336", OK, NULL, "Some non-essential DMI data (e.g. serial numbers) is overwritten when using flashrom. This is an OEM board used by HP (e.g. dx2300 Microtower)."),
+ B("MSI", "MS-7345 (P35 Neo2-FIR)", OK, "http://www.msi.com/product/mb/P35-Neo2-FR---FIR.html", NULL),
+ B("MSI", "MS-7357 (G33M)", OK, "http://www.msi.com/product/mb/G33M.html", NULL),
+ B("MSI", "MS-7368 (K9AG Neo2-Digital)", OK, "http://www.msi.com/product/mb/K9AG-Neo2-Digital.html", NULL),
+ B("MSI", "MS-7369 (K9N Neo V2)", OK, "http://www.msi.com/product/mb/K9N-Neo-V2.html", NULL),
+ B("MSI", "MS-7376 (K9A2 Platinum V1)", OK, "http://www.msi.com/product/mb/K9A2-Platinum.html", NULL),
+ B("MSI", "MS-7379 (G31M)", OK, "http://www.msi.com/product/mb/G31M.html", NULL),
+ B("MSI", "MS-7399 1.1 (Persian)", OK, "http://acersupport.com/acerpanam/desktop/0000/Acer/AspireM5640/AspireM5640sp2.shtml", "This is an OEM board used by Acer in e.g. Aspire M5640/M3640."),
+ B("MSI", "MS-7502", OK, NULL, "This is an OEM board used by Medion in e.g. Medion MD8833."),
+ B("MSI", "MS-7522 (MSI X58 Pro-E)", OK, "http://www.msi.com/product/mb/X58_ProE.html", NULL),
+ B("MSI", "MS-7529 (G31M3-L(S) V2)", OK, "http://www.msi.com/product/mb/G31M3-L-V2---G31M3-LS-V2.html", NULL),
+ B("MSI", "MS-7529 (G31TM-P21)", OK, "http://www.msi.com/product/mb/G31TM-P21.html", NULL),
+ B("MSI", "MS-7548 (Aspen-GL8E)", OK, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c01635688&lc=en&cc=us&dlc=en", NULL),
+ B("MSI", "MS-7551 (KA780G)", OK, "http://www.msi.com/product/mb/KA780G.html", NULL),
+ B("MSI", "MS-7596 (785GM-E51)", OK, "http://www.msi.com/product/mb/785GM-E51.html", NULL),
+ B("MSI", "MS-7597 (GF615M-P33)", BAD, NULL, "Missing board enable/SIO support (Fintek F71889), see https://flashrom.org/pipermail/flashrom/2012-March/008956.html"),
+ B("MSI", "MS-7599 (870-C45)", OK, "http://www.msi.com/product/mb/870-C45.html", NULL),
+ B("MSI", "MS-7613 (Iona-GL8E)", BAD, "http://h10025.www1.hp.com/ewfrf/wc/document?docname=c02014355&lc=en&cc=dk&dlc=en&product=4348478", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("MSI", "MS-7635 (H55M-ED55)", BAD, "http://www.msi.com/product/mb/H55M-ED55.html", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("MSI", "MS-7640 (890FXA-GD70)",OK, "http://www.msi.com/product/mb/890FXA-GD70.html", NULL),
+ B("MSI", "MS-7642 (890GXM-G65)", OK, "http://www.msi.com/product/mb/890GXM-G65.html", NULL),
+ B("MSI", "MS-7676 (H67MA-ED55(B3))", OK, "http://www.msi.com/product/mb/H67MA-ED55--B3-.html", "Seems to work fine basically, but user reported (hopefully unrelated) buggy behavior of the board after a firmware upgrade. See https://flashrom.org/pipermail/flashrom/2012-January/008547.html"),
+ B("MSI", "MS-7676 (Z68MA-G45 (B3))", OK, "http://www.msi.com/product/mb/Z68MA-G45--B3-.html", NULL),
+ B("MSI", "MS-7696 (A75MA-G55)", OK, "http://www.msi.com/product/mb/A75MA-G55.html", NULL),
+ B("MSI", "MS-7698 (E350IA-E45)", OK, "http://www.msi.com/product/mb/E350IA-E45.html", NULL),
+ B("MSI", "MS-7740 (H61MA-E35(B3))", OK, "http://www.msi.com/product/mb/H61MA-E35--B3-.html", NULL),
+ B("MSI", "MS-7756 (H77MA-G43)", OK, "http://www.msi.com/product/mb/H77MA-G43.html", NULL),
+ B("MSI", "MS-7760 (X79A-GD45 (8D))", OK, "http://www.msi.com/product/mb/X79A-GD45-8D.html", NULL),
+ B("MSI", "MS-7808 (B75MA-E33)", OK, "http://www.msi.com/product/mb/B75MA-E33.html", NULL),
+ B("MSI", "MS-7816 (H87-G43)", OK, "http://www.msi.com/product/mb/H87-G43.html", NULL),
+ B("MSI", "MS-7817 (H81M-E33)", OK, "http://www.msi.com/product/mb/H81ME33.html", NULL),
+ B("MSI", "MS-9830 (IM-945GSE-A, A9830IMS)", OK, "http://www.msi.com/product/ipc/IM-945GSE-A.html", NULL),
+ B("NEC", "PowerMate 2000", OK, "http://support.necam.com/mobilesolutions/hardware/Desktops/pm2000/celeron/", NULL),
+ B("Nokia", "IP530", OK, NULL, NULL),
+ B("Palit", "N61S", OK, NULL, NULL),
+ B("PCCHIPS ", "M598LMR (V9.0)", OK, NULL, NULL),
+ B("PCCHIPS ", "M863G (V5.1A)", OK, "http://www.pcchips.com.tw/PCCWebSite/Products/ProductsDetail.aspx?CategoryID=1&DetailID=343&DetailName=Feature&MenuID=1&LanID=0", NULL),
+ B("PC Engines", "Alix.1c", OK, "http://pcengines.ch/alix1c.htm", NULL),
+ B("PC Engines", "Alix.2c2", OK, "http://pcengines.ch/alix2c2.htm", NULL),
+ B("PC Engines", "Alix.2c3", OK, "http://pcengines.ch/alix2c3.htm", NULL),
+ B("PC Engines", "Alix.2d3", OK, "http://pcengines.ch/alix2d3.htm", NULL),
+ B("PC Engines", "Alix.3c3", OK, "http://pcengines.ch/alix3c3.htm", NULL),
+ B("PC Engines", "Alix.3d3", OK, "http://pcengines.ch/alix3d3.htm", NULL),
+ B("PC Engines", "Alix.6f2", OK, "http://pcengines.ch/alix6f2.htm", NULL),
+ B("PC Engines", "APU", OK, "http://pcengines.ch/apu.htm", NULL),
+ B("PC Engines", "WRAP.2E", OK, "http://pcengines.ch/wrap2e1.htm", NULL),
+ B("PCWARE", "APM80-D3", OK, "http://www.pcwarebr.com.br/produtos_mb_apm80-d3.php", "Probably manufactured by ASUS"),
+ B("Pegatron", "IPP7A-CP", OK, NULL, NULL),
+ B("Portwell", "PEB-4700VLA", OK, "http://www.portwell.com/products/detail.asp?CUSTCHAR1=PEB-4700VLA", NULL),
+ B("RCA", "RM4100", OK, "http://www.settoplinux.org/index.php?title=RCA_RM4100", NULL),
+ B("Samsung", "Polaris 32", OK, NULL, NULL),
+ B("SAPPHIRE", "IPC-E350M1", OK, "http://www.sapphiretech.com/presentation/product/?pid=1034&lid=1", NULL),
+ B("Shuttle", "AB61", OK, "http://www.shuttle.eu/_archive/older/de/ab61.htm", NULL),
+ B("Shuttle", "AK31", OK, "http://www.motherboard.cz/mb/shuttle/AK31.htm", NULL),
+ B("Shuttle", "AK38N", OK, "http://eu.shuttle.com/en/desktopdefault.aspx/tabid-36/558_read-9889/", NULL),
+ B("Shuttle", "AV11V30", OK, NULL, NULL),
+ B("Shuttle", "AV18E2", OK, "http://www.shuttle.eu/_archive/older/de/av18.htm", NULL),
+ B("Shuttle", "FB61", OK, "http://www.shuttle.eu/_archive/older/en/fb61.htm#mainboardfb6", "Used in SB61G2 systems."),
+ B("Shuttle", "FD37", OK, "http://www.shuttle.eu/products/discontinued/barebones/sd37p2/", NULL),
+ B("Shuttle", "FH67", OK, "http://www.shuttle.eu/products/mini-pc/sh67h3/specification/", NULL),
+ B("Shuttle", "FN25", OK, "http://www.shuttle.eu/products/discontinued/barebones/sn25p/?0=", NULL),
+ B("Shuttle", "FN78S", OK, "http://www.shuttle.eu/products/discontinued/barebones/sn78sh7/", NULL),
+ B("Shuttle", "X50/X50(B)", OK, "http://au.shuttle.com/product_detail_spec.jsp?PI=1241", NULL),
+ B("Soyo", "SY-5VD", BAD, "http://www.soyo.com/content/Downloads/163/&c=80&p=464&l=English", "No public report found. Owned by Uwe Hermann <uwe@hermann-uwe.de>. May work now."),
+ B("Soyo", "SY-6BA+ III", OK, "http://www.motherboard.cz/mb/soyo/SY-6BA+III.htm", NULL),
+ B("Soyo", "SY-7VCA", OK, "http://www.tomshardware.com/reviews/12-socket-370-motherboards,196-15.html", NULL),
+ B("Sun", "Blade x6250", OK, "http://www.sun.com/servers/blades/x6250/", NULL),
+ B("Sun", "Fire x4150", BAD, "http://www.sun.com/servers/x64/x4150/", "No public report found. May work now."),
+ B("Sun", "Fire x4200", BAD, "http://www.sun.com/servers/entry/x4200/", "No public report found. May work now."),
+ B("Sun", "Fire x4540", BAD, "http://www.sun.com/servers/x64/x4540/", "No public report found. May work now."),
+ B("Sun", "Fire x4600", BAD, "http://www.sun.com/servers/x64/x4600/", "No public report found. May work now."),
+ B("Sun", "Ultra 40 M2", OK, "http://download.oracle.com/docs/cd/E19127-01/ultra40.ws/820-0123-13/intro.html", NULL),
+ B("Supermicro", "A1SAi-2550F", OK, "http://www.supermicro.com/products/motherboard/Atom/X10/A1SAi-2550F.cfm", NULL),
+ B("Supermicro", "H8QC8", OK, "http://www.supermicro.com/Aplus/motherboard/Opteron/nforce/H8QC8.cfm", NULL),
+ B("Supermicro", "H8QME-2", OK, "http://www.supermicro.com/Aplus/motherboard/Opteron8000/MCP55/H8QME-2.cfm", NULL),
+ B("Supermicro", "X10SLM-F", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C220/X10SLM-F.cfm", "Probing works (Winbond W25Q128, 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked; SMM protection enabled."),
+ B("Supermicro", "X5DP8-G2", OK, "http://www.supermicro.com/products/motherboard/Xeon/E7501/X5DP8-G2.cfm", NULL),
+ B("Supermicro", "X7DBT-INF", OK, "http://www.supermicro.com/products/motherboard/Xeon1333/5000P/X7DBT-INF.cfm", NULL),
+ B("Supermicro", "X7DWT", OK, "http://www.supermicro.com/products/motherboard/Xeon1333/5400/X7DWT.cfm", "Used in Dell C6100 servers."),
+ B("Supermicro", "X7SPA-H(F)", OK, "http://www.supermicro.com/products/motherboard/ATOM/ICH9/X7SPA.cfm?typ=H", NULL),
+ B("Supermicro", "X7SPE-HF-D525", OK, "http://www.supermicro.com/products/motherboard/ATOM/ICH9/X7SPE-HF-D525.cfm", NULL),
+ B("Supermicro", "X8DT3", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DT3.cfm", NULL),
+ B("Supermicro", "X8DT6-F", OK, "http://www.supermicro.nl/products/motherboard/QPI/5500/X8DT6-F.cfm?IPMI=Y&SAS=Y", NULL),
+ B("Supermicro", "X8DTE-F", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DT6-F.cfm?IPMI=Y&SAS=N", NULL),
+ B("Supermicro", "X8DTG-D", OK, "http://www.supermicro.com/products/motherboard/qpi/5500/x8dtg-df.cfm", NULL),
+ B("Supermicro", "X8DTH-6F", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTH-6F.cfm", NULL),
+ B("Supermicro", "X8DTT-F", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTT-F.cfm", NULL),
+ B("Supermicro", "X8DTT-HIBQF", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTT-H.cfm", NULL),
+ B("Supermicro", "X8DTU-6TF+", BAD, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTU_.cfm?TYP=SAS&LAN=10", "Probing works (Atmel AT25DF321A, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X8DTU-F", OK, "http://www.supermicro.com/products/motherboard/QPI/5500/X8DTU-F.cfm", NULL),
+ B("Supermicro", "X8SAX", OK, "http://www.supermicro.com/products/motherboard/xeon3000/x58/x8sax.cfm", NULL),
+ B("Supermicro", "X8SIE(-F)", BAD, "http://www.supermicro.com/products/motherboard/Xeon3000/3400/X8SIE.cfm?IPMI=N&TYP=LN2", "Requires unlocking the ME although the registers are set up correctly by the descriptor/BIOS already (tested with swseq and hwseq)."),
+ B("Supermicro", "X8SIL-F", OK, "http://www.supermicro.com/products/motherboard/Xeon3000/3400/X8SIL.cfm", NULL),
+ B("Supermicro", "X8STi", OK, "http://www.supermicro.com/products/motherboard/Xeon3000/X58/X8STi.cfm", NULL),
+ B("Supermicro", "X9DR3-F", BAD, "http://www.supermicro.com/products/motherboard/xeon/c600/x9dr3-f.cfm", "Probing works (Numonyx N25Q128 (supported by SFDP only atm), 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9DRD-7LN4F", BAD, "http://www.supermicro.com/products/motherboard/xeon/c600/x9drd-7ln4f.cfm", "Probing works (Numonyx N25Q128 (supported by SFDP only atm), 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9DRT-HF+", BAD, NULL, "Probing works (Numonyx N25Q128 (supported by SFDP only atm), 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked; SMM protection enabled."),
+ B("Supermicro", "X9DRW", BAD, NULL, "Probing works (Numonyx N25Q128 (supported by SFDP only atm), 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9QRi-F+", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C600/X9QRi-F_.cfm", "Probing works (Macronix MX25L12805, 16384 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked; SMM protection enabled."),
+ B("Supermicro", "X9SCA-F", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C202_C204/X9SCA-F.cfm", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9SCE-F", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C202_C204/X9SCE-F.cfm", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9SCL", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C202_C204/X9SCL.cfm", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Supermicro", "X9SCM-F", BAD, "http://www.supermicro.com/products/motherboard/Xeon/C202_C204/X9SCM-F.cfm", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("T-Online", "S-100", OK, "http://wiki.freifunk-hannover.de/T-Online_S_100", NULL),
+ B("Tekram", "P6Pro-A5", OK, "http://www.motherboard.cz/mb/tekram/P6Pro-A5.htm", NULL),
+ B("Termtek", "TK-3370 (Rev:2.5B)", OK, NULL, NULL),
+ B("Thomson", "IP1000", OK, "http://www.settoplinux.org/index.php?title=Thomson_IP1000", NULL),
+ B("TriGem", "Anaheim-3", OK, "http://www.e4allupgraders.info/dir1/motherboards/socket370/anaheim3.shtml", NULL),
+ B("TriGem", "Lomita", OK, "http://www.e4allupgraders.info/dir1/motherboards/socket370/lomita.shtml", NULL),
+ B("Tyan", "S1846 (Tsunami ATX)", OK, "http://www.tyan.com/archive/products/html/tsunamiatx.html", NULL),
+ B("Tyan", "S2466 (Tiger MPX)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=461", NULL),
+ B("Tyan", "S2498 (Tomcat K7M)", OK, "http://www.tyan.com/archive/products/html/tomcatk7m.html", NULL),
+ B("Tyan", "S2723 (Tiger i7501)", OK, "http://www.tyan.com/archive/products/html/tigeri7501.html", NULL),
+ B("Tyan", "S2875 (Tiger K8W)", OK, "http://www.tyan.com/archive/products/html/tigerk8w.html", NULL),
+ B("Tyan", "S2881 (Thunder K8SR)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=115", NULL),
+ B("Tyan", "S2882-D (Thunder K8SD Pro)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=127", NULL),
+ B("Tyan", "S2882 (Thunder K8S Pro)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=121", NULL),
+ B("Tyan", "S2891 (Thunder K8SRE)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=144", NULL),
+ B("Tyan", "S2892 (Thunder K8SE)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=145", NULL),
+ B("Tyan", "S2895 (Thunder K8WE)", OK, "http://www.tyan.com/archive/products/html/thunderk8we.html", NULL),
+ B("Tyan", "S2912 (Thunder n3600R)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=157", NULL),
+ B("Tyan", "S2915-E (Thunder n6650W)", OK, "http://tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=541&SKU=600000041", NULL),
+ B("Tyan", "S2915 (Thunder n6650W)", OK, "http://tyan.com/product_board_detail.aspx?pid=163", NULL),
+ B("Tyan", "S2933 (Thunder n3600S)", OK, "http://tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=478&SKU=600000063", NULL),
+ B("Tyan", "S3095 (Tomcat i945GM)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=181", NULL),
+ B("Tyan", "S3992 (Thunder h2000M)", OK, "http://tyan.com/product_board_detail.aspx?pid=235", NULL),
+ B("Tyan", "S4882 (Thunder K8QS Pro)", OK, "http://www.tyan.com/archive/products/html/thunderk8qspro.html", NULL),
+ B("Tyan", "S5180 (Toledo i965R)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=456", NULL),
+ B("Tyan", "S5191 (Toledo i3000R)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=343", NULL),
+ B("Tyan", "S5197 (Toledo i3010W)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=349", NULL),
+ B("Tyan", "S5211-1U (Toledo i3200R)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=593", NULL),
+ B("Tyan", "S5211 (Toledo i3210W)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=591", NULL),
+ B("Tyan", "S5220 (Toledo q35T)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=597", NULL),
+ B("Tyan", "S5375-1U (Tempest i5100X)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=610", NULL),
+ B("Tyan", "S5375 (Tempest i5100X)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=566", NULL),
+ B("Tyan", "S5376 (Tempest i5100W)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=605", "Both S5376G2NR and S5376WAG2NR should work."),
+ B("Tyan", "S5377 (Tempest i5100T)", OK, "http://www.tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=642&SKU=600000017", NULL),
+ B("Tyan", "S5382 (Tempest i5000PW)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=439", NULL),
+ B("Tyan", "S5397 (Tempest i5400PW)", OK, "http://www.tyan.com/product_board_detail.aspx?pid=560", NULL),
+ B("Tyan", "S7066 (S7066WGM3NR)", BAD, "http://www.tyan.com/product_SKU_spec.aspx?ProductType=MB&pid=790&SKU=600000330", "Probing works (Winbond W25Q64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("VIA", "EITX-3000", OK, "http://www.viaembedded.com/en/products/boards/810/1/EITX-3000.html", NULL),
+ B("VIA", "EPIA M/MII/...", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=202", NULL), /* EPIA-MII link for now */
+ B("VIA", "EPIA SP", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=261", NULL),
+ B("VIA", "EPIA-CN", OK, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=400", NULL),
+ B("VIA", "EPIA EK", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?motherboard_id=420", NULL),
+ B("VIA", "EPIA-EX15000G", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=450", NULL),
+ B("VIA", "EPIA-LN", OK, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=473", NULL),
+ B("VIA", "EPIA-M700", OK, "http://via.com.tw/servlet/downloadSvl?motherboard_id=670&download_file_id=3700", NULL),
+ B("VIA", "EPIA-N/NL", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=221", NULL), /* EPIA-N link for now */
+ B("VIA", "EPIA-NX15000G", OK, "http://www.via.com.tw/en/products/embedded/ProductDetail.jsp?productLine=1&motherboard_id=470", NULL),
+ B("VIA", "NAB74X0", OK, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=590", NULL),
+ B("VIA", "pc2500e", OK, "http://www.via.com.tw/en/initiatives/empowered/pc2500_mainboard/index.jsp", NULL),
+ B("VIA", "PC3500G", OK, "http://www.via.com.tw/en/initiatives/empowered/pc3500_mainboard/index.jsp", NULL),
+ B("VIA", "VB700X", OK, "http://www.via.com.tw/en/products/mainboards/motherboards.jsp?motherboard_id=490", NULL),
+ B("ZOTAC", "Fusion-ITX WiFi (FUSION350-A-E)", OK, NULL, NULL),
+ B("ZOTAC", "GeForce 8200", OK, NULL, NULL),
+ B("ZOTAC", "H61-ITX WiFi (H61ITX-A-E)", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ZOTAC", "H67-ITX WiFi (H67ITX-C-E)", BAD, NULL, "Probing works (Winbond W25Q32, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("ZOTAC", "IONITX-A-E", OK, NULL, NULL),
+ B("ZOTAC", "IONITX-F-E", OK, NULL, NULL),
+ B("ZOTAC", "nForce 630i Supreme (N73U-Supreme)", OK, NULL, NULL),
+ B("ZOTAC", "ZBOX AD02 (PLUS)", OK, NULL, NULL),
+ B("ZOTAC", "ZBOX HD-ID11", OK, NULL, NULL),
+#endif
+
+ {0},
+};
+
+/* Please keep this list alphabetically ordered by vendor/board. */
+const struct board_info laptops_known[] = {
+#if defined(__i386__) || defined(__x86_64__)
+ B("Acer", "Aspire 1520", OK, "http://support.acer.com/us/en/acerpanam/notebook/0000/Acer/Aspire1520/Aspire1520nv.shtml", NULL),
+ B("Acer", "Aspire One", BAD, NULL, "http://www.coreboot.org/pipermail/coreboot/2009-May/048041.html"),
+ B("ASUS", "A8Jm", OK, NULL, NULL),
+ B("ASUS", "Eee PC 701 4G", BAD, "http://www.asus.com/Eee/Eee_PC/Eee_PC_4G/", "It seems the chip (25X40) is behind some SPI flash translation layer (likely in the EC, the ENE KB3310)."),
+ B("ASUS", "M6Ne", NT, NULL, "Untested board enable."),
+ B("ASUS", "U38N", OK, NULL, NULL),
+ B("Clevo", "P150HM", BAD, "http://www.clevo.com.tw/en/products/prodinfo_2.asp?productid=307", "Probing works (Macronix MX25L3205, 4096 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs), ME region is locked."),
+ B("Dell", "Latitude D630", OK, NULL, NULL),
+ B("Dell", "Inspiron 1420", OK, NULL, NULL),
+ B("Dell", "Latitude CPi A366XT", BAD, "http://www.coreboot.org/Dell_Latitude_CPi_A366XT", "The laptop immediately powers off if you try to hot-swap the chip. It's not yet tested if write/erase would work on this laptop."),
+ B("Dell", "Vostro 3700", BAD, NULL, "Locked ME, see https://flashrom.org/pipermail/flashrom/2012-May/009197.html."),
+ B("Dell", "Latitude E6520", BAD, NULL, "Locked ME, see https://flashrom.org/pipermail/flashrom/2012-June/009420.html."),
+ B("Elitegroup", "A928", OK, NULL, "Bootsector is locked and needs to be skipped with a layout file (writeable address range is 00000000:0003bfff)."),
+ B("Fujitsu", "Amilo Xi 3650", OK, NULL, NULL),
+ B("HP/Compaq", "EliteBook 8560p", BAD, NULL, "SPI lock down, SMM protection, PR in BIOS region, read-only descriptor, locked ME region."),
+ B("HP/Compaq", "nx9005", BAD, "http://h18000.www1.hp.com/products/quickspecs/11602_na/11602_na.HTML", "Shuts down when probing for a chip. https://flashrom.org/pipermail/flashrom/2010-May/003321.html"),
+ B("HP/Compaq", "nx9010", BAD, "http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?lang=en&cc=us&objectID=c00348514", "Hangs upon '''flashrom -V''' (needs hard power-cycle then)."),
+ B("IBM/Lenovo", "ThinkPad T40p", BAD, "http://www.thinkwiki.org/wiki/Category:T40p", NULL),
+ B("IBM/Lenovo", "ThinkPad T410s", BAD, "http://www.thinkwiki.org/wiki/Category:T410s", "Probing works (Winbond W25X64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs) and ME is locked. Also, a Protected Range is locking the top range of the BIOS region (presumably the boot block)."),
+ B("IBM/Lenovo", "ThinkPad T420", BAD, "http://www.thinkwiki.org/wiki/Category:T420", "Probing works (Macronix MX25L6405, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs) and ME is locked. Also, a Protected Range is locking the top range of the BIOS region (presumably the boot block)."),
+ B("IBM/Lenovo", "ThinkPad X1", BAD, "http://www.thinkwiki.org/wiki/Category:X1", "Probing works (ST M25PX64, 8192 kB, SPI), but parts of the flash are problematic: descriptor is r/o (conforming to ICH reqs) and ME is locked. Also, a Protected Range is locking the top range of the BIOS region (presumably the boot block)."),
+ B("IBM/Lenovo", "ThinkPad T530", DEP, "http://www.thinkwiki.org/wiki/Category:T530", "Works fine but only with coreboot (due to locked regions and additional PR restrictions)."),
+ B("IBM/Lenovo", "ThinkPad 240", BAD, "http://www.stanford.edu/~bresnan//tp240.html", "Seems to (partially) work at first, but one block/sector cannot be written which then leaves you with a bricked laptop. Maybe this can be investigated and fixed in software later."),
+ B("IBM/Lenovo", "3000 V100 TF05Cxx", OK, "http://www5.pc.ibm.com/europe/products.nsf/products?openagent&brand=Lenovo3000Notebook&series=Lenovo+3000+V+Series#viewallmodelstop", NULL),
+ //B("MSI", "GT60-2OD", OK, "http://www.msi.com/product/nb/GT60_2OD.html", NULL), requires layout patches
+ B("Teclast", "X98 Air 3G", OK, NULL, NULL),
+#endif
+
+ {0},
+};
+#endif
diff --git a/print_wiki.c b/print_wiki.c
new file mode 100644
index 0000000..d2fb5e2
--- /dev/null
+++ b/print_wiki.c
@@ -0,0 +1,457 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "programmer.h"
+
+static const char wiki_header[] = "= Supported devices =\n\n\
+<div style=\"margin-top:0.5em; padding:0.5em 0.5em 0.5em 0.5em; \
+background-color:#eeeeee; text-align:left; border:1px solid #aabbcc;\">\
+<small>\n\
+'''Last update:''' %s (generated by flashrom %s)<br />\n\
+The tables below are generated from flashrom's source by copying the output of '''flashrom -z'''.<br /><br />\n\
+A short explanation of the cells representing the support state follows:<br />\n\
+{| border=\"0\" valign=\"top\"\n\
+! style=\"text-align:left;\" |\n\
+! style=\"text-align:left;\" |\n\
+|-\n\
+|{{OK}}\n\
+| The feature was '''tested and should work''' in general unless there is a bug in flashrom or another component in \
+the system prohibits some functionality.\n\
+|-\n\
+|{{Dep}}\n\
+| '''Configuration-dependent'''. The feature was tested and should work in general but there are common \
+configurations that drastically limit flashrom's capabilities or make it completely stop working.\n\
+|-\n\
+|{{?3}}\n\
+| The feature is '''untested''' but believed to be working.\n\
+|-\n\
+|{{NA}}\n\
+| The feature is '''not applicable''' in this configuration (e.g. write operations on ROM chips).\n\
+|-\n\
+|{{No}}\n\
+| The feature is '''known to not work'''. Don't bother testing (nor reporting. Patches welcome! ;).\n\
+|}\n\
+</small></div>\n";
+
+static const char th_start[] = "| valign=\"top\"|\n\n\
+{| border=\"0\" style=\"font-size: smaller\" valign=\"top\"\n\
+|- bgcolor=\"#6699dd\"\n";
+
+#if CONFIG_INTERNAL == 1
+static const char chipset_th[] = "\
+! align=\"left\" | Vendor\n\
+! align=\"left\" | Southbridge\n\
+! align=\"center\" | PCI IDs\n\
+! align=\"center\" | Status\n\n";
+
+static const char board_th[] = "\
+! align=\"left\" | Vendor\n\
+! align=\"left\" | Mainboard\n\
+! align=\"left\" | Required option\n\
+! align=\"center\" | Status\n\n";
+
+static const char board_intro[] = "\
+\n== Supported mainboards ==\n\n\
+In general, it is very likely that flashrom works out of the box even if your \
+mainboard is not listed below.\n\nThis is a list of mainboards where we have \
+verified that they either do or do not need any special initialization to \
+make flashrom work (given flashrom supports the respective chipset and flash \
+chip), or that they do not yet work at all. If they do not work, support may \
+or may not be added later.\n\n\
+Mainboards (or individual revisions) which don't appear in the list may or may \
+not work (we don't know, someone has to give it a try). Please report any \
+further verified mainboards on the [[Mailinglist|mailing list]].\n";
+#endif
+
+static const char chip_th[] = "\
+! align=\"left\" | Vendor\n\
+! align=\"left\" | Device\n\
+! align=\"center\" | Size [kB]\n\
+! align=\"center\" | Type\n\
+! align=\"center\" colspan=\"4\" | Status\n\
+! align=\"center\" colspan=\"2\" | Voltage [V]\n\n\
+|- bgcolor=\"#6699ff\"\n| colspan=\"4\" | &nbsp;\n\
+| Probe\n| Read\n| Erase\n| Write\n\
+| align=\"center\" | Min \n| align=\"center\" | Max\n\n";
+
+static const char chip_intro[] = "\
+\n== Supported flash chips ==\n\n\
+The list below contains all chips that have some kind of explicit support added to flashrom and their last \
+known test status. Newer SPI flash chips might work even without explicit support if they implement SFDP ([\
+http://www.jedec.org/standards-documents/docs/jesd216 Serial Flash Discoverable Parameters - JESD216]). \
+Flashrom will detect this automatically and inform you about it.\n\n\
+The names used below are designed to be as concise as possible and hence contain only the characters \
+describing properties that are relevant to flashrom. Irrelevant characters specify attributes flashrom can not \
+use or even detect by itself (e.g. the physical package) and have no effect on flashrom's operation. They are \
+replaced by dots ('.') functioning as wildcards (like in Regular Expressions) or are completely omitted at the \
+end of a name.\n";
+
+static const char programmer_th[] = "\
+! align=\"left\" | Programmer\n\
+! align=\"left\" | Vendor\n\
+! align=\"left\" | Device\n\
+! align=\"center\" | IDs\n\
+! align=\"center\" | Status\n\n";
+
+/* The output of this module relies on MediaWiki templates to select special formatting styles for table cells
+ * reflecting the test status of the respective hardware. This functions returns the correct template name for
+ * the supplied enum test_state. */
+static const char *test_state_to_template(enum test_state test_state)
+{
+ switch (test_state) {
+ case OK: return "OK";
+ case BAD: return "No";
+ case NA: return "NA";
+ case DEP: return "Dep";
+ case NT:
+ default: return "?3";
+ }
+}
+
+#if CONFIG_INTERNAL == 1
+static const char laptop_intro[] = "\n== Supported mobile devices (laptops, tablets etc.) ==\n\n\
+In general, flashing mobile devices is more difficult because they\n\n\
+* often use the flash chip for stuff besides the BIOS,\n\
+* often have special protection stuff which has to be handled by flashrom,\n\
+* often use flash translation circuits which need drivers in flashrom.\n\n\
+<div style=\"margin-top:0.5em; padding:0.5em 0.5em 0.5em 0.5em; \
+background-color:#ff6666; align:right; border:1px solid #000000;\">\n\
+'''IMPORTANT:''' At this point we recommend to '''not''' use flashrom on \
+untested mobile devices unless you have a means to recover from a flashing that goes \
+wrong (a working backup flash chip and/or good soldering skills).\n</div>\n";
+
+static void print_supported_chipsets_wiki(int cols)
+{
+ int i;
+ unsigned int lines_per_col;
+ const struct penable *e;
+ int enablescount = 0, color = 1;
+
+ for (e = chipset_enables; e->vendor_name != NULL; e++)
+ enablescount++;
+
+ /* +1 to force the resulting number of columns to be < cols */
+ lines_per_col = enablescount / cols + ((enablescount%cols) > 0 ? 1 : 0);
+
+ printf("\n== Supported chipsets ==\n\nTotal amount of supported chipsets: '''%d'''\n\n"
+ "{| border=\"0\" valign=\"top\"\n", enablescount);
+
+ e = chipset_enables;
+ for (i = 0; e[i].vendor_name != NULL; i++) {
+ if ((i % lines_per_col) == 0)
+ printf("%s%s", th_start, chipset_th);
+
+ /* Alternate colors if the vendor changes. */
+ if (i > 0 && strcmp(e[i].vendor_name, e[i - 1].vendor_name))
+ color = !color;
+
+ printf("|- bgcolor=\"#%s\"\n| %s || %s "
+ "|| %04x:%04x || {{%s}}\n", (color) ? "eeeeee" : "dddddd",
+ e[i].vendor_name, e[i].device_name,
+ e[i].vendor_id, e[i].device_id,
+ test_state_to_template(e[i].status));
+
+ if (((i % lines_per_col) + 1) == lines_per_col)
+ printf("\n|}\n\n");
+ }
+
+ /* end inner table if it did not fill the last column fully */
+ if (((i % lines_per_col)) > 0)
+ printf("\n|}\n\n");
+ printf("\n\n|}\n");
+}
+
+static void print_supported_boards_wiki_helper(const char *devicetype, int cols, const struct board_info boards[])
+{
+ int i, k;
+ unsigned int boardcount, lines_per_col;
+ unsigned int boardcount_good = 0, boardcount_bad = 0, boardcount_nt = 0;
+ int num_notes = 0, color = 1;
+ char *notes = calloc(1, 1);
+ char tmp[900 + 1];
+ const struct board_match *b = board_matches;
+
+ for (i = 0; boards[i].vendor != NULL; i++) {
+ if (boards[i].working == OK)
+ boardcount_good++;
+ else if (boards[i].working == NT)
+ boardcount_nt++;
+ else
+ boardcount_bad++;
+ }
+ boardcount = boardcount_good + boardcount_nt + boardcount_bad;
+
+ /* +1 to force the resulting number of columns to be < cols */
+ lines_per_col = boardcount / cols + ((boardcount%cols) > 0 ? 1 : 0);
+
+ printf("\n\nTotal amount of known good %s: '''%d'''; "
+ "Untested (e.g. user vanished before testing new code): '''%d'''; "
+ "Not yet supported (i.e. known-bad): '''%d'''.\n\n"
+ "{| border=\"0\" valign=\"top\"\n", devicetype, boardcount_good, boardcount_nt, boardcount_bad);
+
+ for (i = 0; boards[i].vendor != NULL; i++) {
+ if ((i % lines_per_col) == 0)
+ printf("%s%s", th_start, board_th);
+
+ /* Alternate colors if the vendor changes. */
+ if (i > 0 && strcmp(boards[i].vendor, boards[i - 1].vendor))
+ color = !color;
+
+ k = 0;
+ while ((b[k].vendor_name != NULL) &&
+ (strcmp(b[k].vendor_name, boards[i].vendor) ||
+ strcmp(b[k].board_name, boards[i].name))) {
+ k++;
+ }
+
+ printf("|- bgcolor=\"#%s\"\n| %s || %s%s %s%s || %s%s%s%s "
+ "|| {{%s}}", (color) ? "eeeeee" : "dddddd",
+ boards[i].vendor,
+ boards[i].url ? "[" : "",
+ boards[i].url ? boards[i].url : "",
+ boards[i].name,
+ boards[i].url ? "]" : "",
+ b[k].lb_vendor ? "-p internal:mainboard=" : "&mdash;",
+ b[k].lb_vendor ? b[k].lb_vendor : "",
+ b[k].lb_vendor ? ":" : "",
+ b[k].lb_vendor ? b[k].lb_part : "",
+ test_state_to_template(boards[i].working));
+
+ if (boards[i].note) {
+ num_notes++;
+ printf(" <span id=\"%s_ref%d\"><sup>[[#%s_note%d|%d]]</sup></span>\n",
+ devicetype, num_notes, devicetype, num_notes, num_notes);
+ int ret = snprintf(tmp, sizeof(tmp),
+ "<span id=\"%s_note%d\">%d. [[#%s_ref%d|&#x2191;]]</span>"
+ " <nowiki>%s</nowiki><br />\n", devicetype, num_notes, num_notes,
+ devicetype, num_notes, boards[i].note);
+ if (ret < 0 || ret >= sizeof(tmp)) {
+ fprintf(stderr, "Footnote text #%d of %s truncated (ret=%d, sizeof(tmp)=%zu)\n",
+ num_notes, devicetype, ret, sizeof(tmp));
+ }
+ notes = strcat_realloc(notes, tmp);
+ } else {
+ printf("\n");
+ }
+
+ if (((i % lines_per_col) + 1) == lines_per_col)
+ printf("\n|}\n\n");
+ }
+
+ /* end inner table if it did not fill the last column fully */
+ if (((i % lines_per_col)) > 0)
+ printf("\n|}\n\n");
+ printf("|}\n");
+
+ if (num_notes > 0)
+ printf("\n<small>\n%s</small>\n", notes);
+ free(notes);
+}
+
+static void print_supported_boards_wiki(void)
+{
+ printf("%s", board_intro);
+ print_supported_boards_wiki_helper("mainboards", 2, boards_known);
+
+ printf("%s", laptop_intro);
+ print_supported_boards_wiki_helper("mobile devices", 1, laptops_known);
+}
+#endif
+
+static void print_supported_chips_wiki(int cols)
+{
+ unsigned int lines_per_col;
+ char *s;
+ char vmax[6];
+ char vmin[6];
+ const struct flashchip *f, *old = NULL;
+ int i = 0, c = 1, chipcount = 0;
+
+ for (f = flashchips; f->name != NULL; f++) {
+ /* Don't count generic entries. */
+ if (!strncmp(f->vendor, "Unknown", 7) ||
+ !strncmp(f->vendor, "Programmer", 10) ||
+ !strncmp(f->name, "unknown", 7))
+ continue;
+ chipcount++;
+ }
+
+ /* +1 to force the resulting number of columns to be < cols */
+ lines_per_col = chipcount / cols + ((chipcount%cols) > 0 ? 1 : 0);
+
+ printf("%s", chip_intro);
+ printf("\nTotal amount of supported chips: '''%d'''\n\n"
+ "{| border=\"0\" valign=\"top\"\n", chipcount);
+
+ for (f = flashchips; f->name != NULL; f++) {
+ /* Don't print generic entries. */
+ if (!strncmp(f->vendor, "Unknown", 7) ||
+ !strncmp(f->vendor, "Programmer", 10) ||
+ !strncmp(f->name, "unknown", 7))
+ continue;
+
+ if ((i % lines_per_col) == 0)
+ printf("%s%s", th_start, chip_th);
+
+ /* Alternate colors if the vendor changes. */
+ if (old != NULL && strcmp(old->vendor, f->vendor))
+ c = !c;
+
+ old = f;
+ s = flashbuses_to_text(f->bustype);
+ sprintf(vmin, "%0.03f", f->voltage.min / (double)1000);
+ sprintf(vmax, "%0.03f", f->voltage.max / (double)1000);
+ printf("|- bgcolor=\"#%s\"\n| %s || %s || align=\"right\" | %d "
+ "|| %s || {{%s}} || {{%s}} || {{%s}} || {{%s}}"
+ "|| %s || %s \n",
+ (c == 1) ? "eeeeee" : "dddddd", f->vendor, f->name,
+ f->total_size, s,
+ test_state_to_template(f->tested.probe),
+ test_state_to_template(f->tested.read),
+ test_state_to_template(f->tested.erase),
+ test_state_to_template(f->tested.write),
+ f->voltage.min ? vmin : "?",
+ f->voltage.max ? vmax : "?");
+ free(s);
+
+ if (((i % lines_per_col) + 1) == lines_per_col)
+ printf("\n|}\n\n");
+ i++;
+ }
+ /* end inner table if it did not fill the last column fully */
+ if (((i % lines_per_col)) > 0)
+ printf("\n|}\n\n");
+ printf("|}\n\n");
+}
+
+/* Following functions are not needed when no PCI/USB programmers are compiled in,
+ * but since print_wiki code has no size constraints we include it unconditionally. */
+static int count_supported_devs_wiki(const struct dev_entry *devs)
+{
+ unsigned int count = 0;
+ unsigned int i = 0;
+ for (i = 0; devs[i].vendor_id != 0; i++)
+ count++;
+ return count;
+}
+
+static void print_supported_devs_wiki_helper(const struct programmer_entry prog)
+{
+ int i = 0;
+ static int c = 0;
+ const struct dev_entry *devs = prog.devs.dev;
+ const unsigned int count = count_supported_devs_wiki(devs);
+
+ /* Alternate colors if the vendor changes. */
+ c = !c;
+
+ for (i = 0; devs[i].vendor_id != 0; i++) {
+ printf("|- bgcolor=\"#%s\"\n", (c) ? "eeeeee" : "dddddd");
+ if (i == 0)
+ printf("| rowspan=\"%u\" | %s |", count, prog.name);
+ printf("| %s || %s || %04x:%04x || {{%s}}\n", devs[i].vendor_name, devs[i].device_name,
+ devs[i].vendor_id, devs[i].device_id, test_state_to_template(devs[i].status));
+ }
+}
+
+static void print_supported_devs_wiki()
+{
+ unsigned int pci_count = 0;
+ unsigned int usb_count = 0;
+ unsigned int i;
+
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ const struct programmer_entry prog = programmer_table[i];
+ switch (prog.type) {
+ case USB:
+ usb_count += count_supported_devs_wiki(prog.devs.dev);
+ break;
+ case PCI:
+ pci_count += count_supported_devs_wiki(prog.devs.dev);
+ break;
+ case OTHER:
+ default:
+ break;
+ }
+ }
+
+ printf("\n== PCI Devices ==\n\n"
+ "Total amount of supported PCI devices flashrom can use as a programmer: '''%d'''\n\n"
+ "{%s%s", pci_count, th_start, programmer_th);
+
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ const struct programmer_entry prog = programmer_table[i];
+ if (prog.type == PCI) {
+ print_supported_devs_wiki_helper(prog);
+ }
+ }
+ printf("\n|}\n\n|}\n");
+
+ printf("\n== USB Devices ==\n\n"
+ "Total amount of supported USB devices flashrom can use as a programmer: '''%d'''\n\n"
+ "{%s%s", usb_count, th_start, programmer_th);
+
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ const struct programmer_entry prog = programmer_table[i];
+ if (prog.type == USB) {
+ print_supported_devs_wiki_helper(prog);
+ }
+ }
+ printf("\n|}\n\n|}\n");
+
+ printf("\n== Other programmers ==\n\n"
+ "{%s", th_start);
+ printf("! align=\"left\" | Programmer\n"
+ "! align=\"left\" | Note\n\n");
+
+ for (i = 0; i < PROGRAMMER_INVALID; i++) {
+ static int c = 0;
+ const struct programmer_entry prog = programmer_table[i];
+ if (prog.type == OTHER && prog.devs.note != NULL) {
+ c = !c;
+ printf("|- bgcolor=\"#%s\"\n", (c) ? "eeeeee" : "dddddd");
+ printf("| %s || %s", prog.name, prog.devs.note);
+ }
+ }
+ printf("\n|}\n\n|}\n");
+}
+
+void print_supported_wiki(void)
+{
+ time_t t = time(NULL);
+ char buf[sizeof("1986-02-28T12:37:42Z")];
+ strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
+
+ printf(wiki_header, buf, flashrom_version);
+ print_supported_chips_wiki(2);
+#if CONFIG_INTERNAL == 1
+ print_supported_chipsets_wiki(3);
+ print_supported_boards_wiki();
+#endif
+ print_supported_devs_wiki();
+}
+
diff --git a/processor_enable.c b/processor_enable.c
new file mode 100644
index 0000000..117aa1e
--- /dev/null
+++ b/processor_enable.c
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the processor specific flash enables and system settings.
+ */
+
+#include "flash.h"
+#include "programmer.h"
+
+#if defined (__MIPSEL__) && defined (__linux)
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static int is_loongson(void)
+{
+ FILE *cpuinfo;
+ cpuinfo = fopen("/proc/cpuinfo", "rb");
+ if (!cpuinfo)
+ return 0;
+ while (!feof(cpuinfo)) {
+ char line[512], *ptr;
+ if (fgets(line, sizeof(line), cpuinfo) == NULL)
+ break;
+ ptr = line;
+ while (*ptr && isspace((unsigned char)*ptr))
+ ptr++;
+ /* "cpu" part appears only with some Linux versions. */
+ if (strncmp(ptr, "cpu", strlen("cpu")) == 0)
+ ptr += strlen("cpu");
+ while (*ptr && isspace((unsigned char)*ptr))
+ ptr++;
+ if (strncmp(ptr, "model", strlen("model")) != 0)
+ continue;
+ ptr += strlen("model");
+ while (*ptr && isspace((unsigned char)*ptr))
+ ptr++;
+ if (*ptr != ':')
+ continue;
+ ptr++;
+ while (*ptr && isspace((unsigned char)*ptr))
+ ptr++;
+ (void)fclose(cpuinfo);
+ return (strncmp(ptr, "ICT Loongson-2 V0.3", strlen("ICT Loongson-2 V0.3")) == 0) ||
+ (strncmp(ptr, "Godson2 V0.3 FPU V0.1", strlen("Godson2 V0.3 FPU V0.1")) == 0);
+ }
+ (void)fclose(cpuinfo);
+ return 0;
+}
+#endif
+
+int processor_flash_enable(void)
+{
+ /* Default to 1 to catch not implemented architectures. */
+ int ret = 1;
+
+ /* FIXME: detect loongson on FreeBSD and OpenBSD as well. */
+#if defined (__MIPSEL__) && defined (__linux)
+ if (is_loongson()) {
+ flashbase = 0x1fc00000;
+ ret = 0;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ /* On x86, flash access is not processor specific except on
+ * AMD Elan SC520, AMD Geode and maybe other SoC-style CPUs.
+ * FIXME: Move enable_flash_cs5536 and get_flashbase_sc520 here.
+ */
+ ret = 0;
+#endif
+ return ret;
+}
diff --git a/programmer.c b/programmer.c
new file mode 100644
index 0000000..1b27c3c
--- /dev/null
+++ b/programmer.c
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010,2011 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "programmer.h"
+
+/* No-op shutdown() for programmers which don't need special handling */
+int noop_shutdown(void)
+{
+ return 0;
+}
+
+/* Fallback map() for programmers which don't need special handling */
+void *fallback_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ /* FIXME: Should return phys_addr. */
+ return NULL;
+}
+
+/* No-op/fallback unmap() for programmers which don't need special handling */
+void fallback_unmap(void *virt_addr, size_t len)
+{
+}
+
+/* No-op chip_writeb() for parallel style drivers not supporting writes */
+void noop_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+}
+
+/* Little-endian fallback for drivers not supporting 16 bit accesses */
+void fallback_chip_writew(const struct flashctx *flash, uint16_t val,
+ chipaddr addr)
+{
+ chip_writeb(flash, val & 0xff, addr);
+ chip_writeb(flash, (val >> 8) & 0xff, addr + 1);
+}
+
+/* Little-endian fallback for drivers not supporting 16 bit accesses */
+uint16_t fallback_chip_readw(const struct flashctx *flash, const chipaddr addr)
+{
+ uint16_t val;
+ val = chip_readb(flash, addr);
+ val |= chip_readb(flash, addr + 1) << 8;
+ return val;
+}
+
+/* Little-endian fallback for drivers not supporting 32 bit accesses */
+void fallback_chip_writel(const struct flashctx *flash, uint32_t val,
+ chipaddr addr)
+{
+ chip_writew(flash, val & 0xffff, addr);
+ chip_writew(flash, (val >> 16) & 0xffff, addr + 2);
+}
+
+/* Little-endian fallback for drivers not supporting 32 bit accesses */
+uint32_t fallback_chip_readl(const struct flashctx *flash, const chipaddr addr)
+{
+ uint32_t val;
+ val = chip_readw(flash, addr);
+ val |= chip_readw(flash, addr + 2) << 16;
+ return val;
+}
+
+void fallback_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len)
+{
+ size_t i;
+ for (i = 0; i < len; i++)
+ chip_writeb(flash, buf[i], addr + i);
+ return;
+}
+
+void fallback_chip_readn(const struct flashctx *flash, uint8_t *buf,
+ chipaddr addr, size_t len)
+{
+ size_t i;
+ for (i = 0; i < len; i++)
+ buf[i] = chip_readb(flash, addr + i);
+ return;
+}
+
+int register_par_master(const struct par_master *mst,
+ const enum chipbustype buses)
+{
+ struct registered_master rmst;
+ if (!mst->chip_writeb || !mst->chip_writew || !mst->chip_writel ||
+ !mst->chip_writen || !mst->chip_readb || !mst->chip_readw ||
+ !mst->chip_readl || !mst->chip_readn) {
+ msg_perr("%s called with incomplete master definition. "
+ "Please report a bug at flashrom@flashrom.org\n",
+ __func__);
+ return ERROR_FLASHROM_BUG;
+ }
+
+ rmst.buses_supported = buses;
+ rmst.par = *mst;
+ return register_master(&rmst);
+}
+
+/* The limit of 4 is totally arbitrary. */
+#define MASTERS_MAX 4
+struct registered_master registered_masters[MASTERS_MAX];
+int registered_master_count = 0;
+
+/* This function copies the struct registered_master parameter. */
+int register_master(const struct registered_master *mst)
+{
+ if (registered_master_count >= MASTERS_MAX) {
+ msg_perr("Tried to register more than %i master "
+ "interfaces.\n", MASTERS_MAX);
+ return ERROR_FLASHROM_LIMIT;
+ }
+ registered_masters[registered_master_count] = *mst;
+ registered_master_count++;
+
+ return 0;
+}
+
+enum chipbustype get_buses_supported(void)
+{
+ int i;
+ enum chipbustype ret = BUS_NONE;
+
+ for (i = 0; i < registered_master_count; i++)
+ ret |= registered_masters[i].buses_supported;
+
+ return ret;
+}
diff --git a/programmer.h b/programmer.h
new file mode 100644
index 0000000..bd8e98d
--- /dev/null
+++ b/programmer.h
@@ -0,0 +1,782 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2000 Ronald G. Minnich <rminnich@gmail.com>
+ * Copyright (C) 2005-2009 coresystems GmbH
+ * Copyright (C) 2006-2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __PROGRAMMER_H__
+#define __PROGRAMMER_H__ 1
+
+#include "flash.h" /* for chipaddr and flashctx */
+
+enum programmer {
+#if CONFIG_INTERNAL == 1
+ PROGRAMMER_INTERNAL,
+#endif
+#if CONFIG_DUMMY == 1
+ PROGRAMMER_DUMMY,
+#endif
+#if CONFIG_NIC3COM == 1
+ PROGRAMMER_NIC3COM,
+#endif
+#if CONFIG_NICREALTEK == 1
+ PROGRAMMER_NICREALTEK,
+#endif
+#if CONFIG_NICNATSEMI == 1
+ PROGRAMMER_NICNATSEMI,
+#endif
+#if CONFIG_GFXNVIDIA == 1
+ PROGRAMMER_GFXNVIDIA,
+#endif
+#if CONFIG_DRKAISER == 1
+ PROGRAMMER_DRKAISER,
+#endif
+#if CONFIG_SATASII == 1
+ PROGRAMMER_SATASII,
+#endif
+#if CONFIG_ATAHPT == 1
+ PROGRAMMER_ATAHPT,
+#endif
+#if CONFIG_ATAVIA == 1
+ PROGRAMMER_ATAVIA,
+#endif
+#if CONFIG_ATAPROMISE == 1
+ PROGRAMMER_ATAPROMISE,
+#endif
+#if CONFIG_IT8212 == 1
+ PROGRAMMER_IT8212,
+#endif
+#if CONFIG_FT2232_SPI == 1
+ PROGRAMMER_FT2232_SPI,
+#endif
+#if CONFIG_SERPROG == 1
+ PROGRAMMER_SERPROG,
+#endif
+#if CONFIG_BUSPIRATE_SPI == 1
+ PROGRAMMER_BUSPIRATE_SPI,
+#endif
+#if CONFIG_DEDIPROG == 1
+ PROGRAMMER_DEDIPROG,
+#endif
+#if CONFIG_RAYER_SPI == 1
+ PROGRAMMER_RAYER_SPI,
+#endif
+#if CONFIG_PONY_SPI == 1
+ PROGRAMMER_PONY_SPI,
+#endif
+#if CONFIG_NICINTEL == 1
+ PROGRAMMER_NICINTEL,
+#endif
+#if CONFIG_NICINTEL_SPI == 1
+ PROGRAMMER_NICINTEL_SPI,
+#endif
+#if CONFIG_NICINTEL_EEPROM == 1
+ PROGRAMMER_NICINTEL_EEPROM,
+#endif
+#if CONFIG_OGP_SPI == 1
+ PROGRAMMER_OGP_SPI,
+#endif
+#if CONFIG_SATAMV == 1
+ PROGRAMMER_SATAMV,
+#endif
+#if CONFIG_LINUX_SPI == 1
+ PROGRAMMER_LINUX_SPI,
+#endif
+#if CONFIG_USBBLASTER_SPI == 1
+ PROGRAMMER_USBBLASTER_SPI,
+#endif
+#if CONFIG_MSTARDDC_SPI == 1
+ PROGRAMMER_MSTARDDC_SPI,
+#endif
+#if CONFIG_PICKIT2_SPI == 1
+ PROGRAMMER_PICKIT2_SPI,
+#endif
+#if CONFIG_CH341A_SPI == 1
+ PROGRAMMER_CH341A_SPI,
+#endif
+ PROGRAMMER_INVALID /* This must always be the last entry. */
+};
+
+enum programmer_type {
+ PCI = 1, /* to detect uninitialized values */
+ USB,
+ OTHER,
+};
+
+struct dev_entry {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ const enum test_state status;
+ const char *vendor_name;
+ const char *device_name;
+};
+
+struct programmer_entry {
+ const char *name;
+ const enum programmer_type type;
+ union {
+ const struct dev_entry *const dev;
+ const char *const note;
+ } devs;
+
+ int (*init) (void);
+
+ void *(*map_flash_region) (const char *descr, uintptr_t phys_addr, size_t len);
+ void (*unmap_flash_region) (void *virt_addr, size_t len);
+
+ void (*delay) (unsigned int usecs);
+};
+
+extern const struct programmer_entry programmer_table[];
+
+int programmer_init(enum programmer prog, const char *param);
+int programmer_shutdown(void);
+
+enum bitbang_spi_master_type {
+ BITBANG_SPI_INVALID = 0, /* This must always be the first entry. */
+#if CONFIG_RAYER_SPI == 1
+ BITBANG_SPI_MASTER_RAYER,
+#endif
+#if CONFIG_PONY_SPI == 1
+ BITBANG_SPI_MASTER_PONY,
+#endif
+#if CONFIG_NICINTEL_SPI == 1
+ BITBANG_SPI_MASTER_NICINTEL,
+#endif
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+ BITBANG_SPI_MASTER_MCP,
+#endif
+#endif
+#if CONFIG_OGP_SPI == 1
+ BITBANG_SPI_MASTER_OGP,
+#endif
+};
+
+struct bitbang_spi_master {
+ enum bitbang_spi_master_type type;
+
+ /* Note that CS# is active low, so val=0 means the chip is active. */
+ void (*set_cs) (int val);
+ void (*set_sck) (int val);
+ void (*set_mosi) (int val);
+ int (*get_miso) (void);
+ void (*request_bus) (void);
+ void (*release_bus) (void);
+ /* Length of half a clock period in usecs. */
+ unsigned int half_period;
+};
+
+#if NEED_PCI == 1
+struct pci_dev;
+
+/* pcidev.c */
+// FIXME: This needs to be local, not global(?)
+extern struct pci_access *pacc;
+int pci_init_common(void);
+uintptr_t pcidev_readbar(struct pci_dev *dev, int bar);
+struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar);
+/* rpci_write_* are reversible writes. The original PCI config space register
+ * contents will be restored on shutdown.
+ */
+int rpci_write_byte(struct pci_dev *dev, int reg, uint8_t data);
+int rpci_write_word(struct pci_dev *dev, int reg, uint16_t data);
+int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data);
+#endif
+
+#if CONFIG_INTERNAL == 1
+struct penable {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ const enum test_state status;
+ const char *vendor_name;
+ const char *device_name;
+ int (*doit) (struct pci_dev *dev, const char *name);
+};
+
+extern const struct penable chipset_enables[];
+
+enum board_match_phase {
+ P1,
+ P2,
+ P3
+};
+
+struct board_match {
+ /* Any device, but make it sensible, like the ISA bridge. */
+ uint16_t first_vendor;
+ uint16_t first_device;
+ uint16_t first_card_vendor;
+ uint16_t first_card_device;
+
+ /* Any device, but make it sensible, like
+ * the host bridge. May be NULL.
+ */
+ uint16_t second_vendor;
+ uint16_t second_device;
+ uint16_t second_card_vendor;
+ uint16_t second_card_device;
+
+ /* Pattern to match DMI entries. May be NULL. */
+ const char *dmi_pattern;
+
+ /* The vendor / part name from the coreboot table. May be NULL. */
+ const char *lb_vendor;
+ const char *lb_part;
+
+ enum board_match_phase phase;
+
+ const char *vendor_name;
+ const char *board_name;
+
+ int max_rom_decode_parallel;
+ const enum test_state status;
+ int (*enable) (void); /* May be NULL. */
+};
+
+extern const struct board_match board_matches[];
+
+struct board_info {
+ const char *vendor;
+ const char *name;
+ const enum test_state working;
+#ifdef CONFIG_PRINT_WIKI
+ const char *url;
+ const char *note;
+#endif
+};
+
+extern const struct board_info boards_known[];
+extern const struct board_info laptops_known[];
+#endif
+
+/* udelay.c */
+void myusec_delay(unsigned int usecs);
+void myusec_calibrate_delay(void);
+void internal_sleep(unsigned int usecs);
+void internal_delay(unsigned int usecs);
+
+#if CONFIG_INTERNAL == 1
+/* board_enable.c */
+int selfcheck_board_enables(void);
+int board_parse_parameter(const char *boardstring, const char **vendor, const char **model);
+void w836xx_ext_enter(uint16_t port);
+void w836xx_ext_leave(uint16_t port);
+void probe_superio_winbond(void);
+int it8705f_write_enable(uint8_t port);
+uint8_t sio_read(uint16_t port, uint8_t reg);
+void sio_write(uint16_t port, uint8_t reg, uint8_t data);
+void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask);
+void board_handle_before_superio(void);
+void board_handle_before_laptop(void);
+int board_flash_enable(const char *vendor, const char *model, const char *cb_vendor, const char *cb_model);
+
+/* chipset_enable.c */
+int chipset_flash_enable(void);
+
+/* processor_enable.c */
+int processor_flash_enable(void);
+#endif
+
+/* physmap.c */
+void *physmap(const char *descr, uintptr_t phys_addr, size_t len);
+void *rphysmap(const char *descr, uintptr_t phys_addr, size_t len);
+void *physmap_ro(const char *descr, uintptr_t phys_addr, size_t len);
+void *physmap_ro_unaligned(const char *descr, uintptr_t phys_addr, size_t len);
+void physunmap(void *virt_addr, size_t len);
+void physunmap_unaligned(void *virt_addr, size_t len);
+#if CONFIG_INTERNAL == 1
+int setup_cpu_msr(int cpu);
+void cleanup_cpu_msr(void);
+
+/* cbtable.c */
+int cb_parse_table(const char **vendor, const char **model);
+int cb_check_image(uint8_t *bios, int size);
+
+/* dmi.c */
+#if defined(__i386__) || defined(__x86_64__)
+extern int has_dmi_support;
+void dmi_init(void);
+int dmi_match(const char *pattern);
+#endif // defined(__i386__) || defined(__x86_64__)
+
+/* internal.c */
+struct superio {
+ uint16_t vendor;
+ uint16_t port;
+ uint16_t model;
+};
+extern struct superio superios[];
+extern int superio_count;
+#define SUPERIO_VENDOR_NONE 0x0
+#define SUPERIO_VENDOR_ITE 0x1
+#define SUPERIO_VENDOR_WINBOND 0x2
+#endif
+#if NEED_PCI == 1
+struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t devclass);
+struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device);
+struct pci_dev *pci_card_find(uint16_t vendor, uint16_t device,
+ uint16_t card_vendor, uint16_t card_device);
+#endif
+int rget_io_perms(void);
+#if CONFIG_INTERNAL == 1
+extern int is_laptop;
+extern int laptop_ok;
+extern int force_boardenable;
+extern int force_boardmismatch;
+void probe_superio(void);
+int register_superio(struct superio s);
+extern enum chipbustype internal_buses_supported;
+int internal_init(void);
+#endif
+
+/* hwaccess.c */
+void mmio_writeb(uint8_t val, void *addr);
+void mmio_writew(uint16_t val, void *addr);
+void mmio_writel(uint32_t val, void *addr);
+uint8_t mmio_readb(void *addr);
+uint16_t mmio_readw(void *addr);
+uint32_t mmio_readl(void *addr);
+void mmio_readn(void *addr, uint8_t *buf, size_t len);
+void mmio_le_writeb(uint8_t val, void *addr);
+void mmio_le_writew(uint16_t val, void *addr);
+void mmio_le_writel(uint32_t val, void *addr);
+uint8_t mmio_le_readb(void *addr);
+uint16_t mmio_le_readw(void *addr);
+uint32_t mmio_le_readl(void *addr);
+#define pci_mmio_writeb mmio_le_writeb
+#define pci_mmio_writew mmio_le_writew
+#define pci_mmio_writel mmio_le_writel
+#define pci_mmio_readb mmio_le_readb
+#define pci_mmio_readw mmio_le_readw
+#define pci_mmio_readl mmio_le_readl
+void rmmio_writeb(uint8_t val, void *addr);
+void rmmio_writew(uint16_t val, void *addr);
+void rmmio_writel(uint32_t val, void *addr);
+void rmmio_le_writeb(uint8_t val, void *addr);
+void rmmio_le_writew(uint16_t val, void *addr);
+void rmmio_le_writel(uint32_t val, void *addr);
+#define pci_rmmio_writeb rmmio_le_writeb
+#define pci_rmmio_writew rmmio_le_writew
+#define pci_rmmio_writel rmmio_le_writel
+void rmmio_valb(void *addr);
+void rmmio_valw(void *addr);
+void rmmio_vall(void *addr);
+
+/* dummyflasher.c */
+#if CONFIG_DUMMY == 1
+int dummy_init(void);
+void *dummy_map(const char *descr, uintptr_t phys_addr, size_t len);
+void dummy_unmap(void *virt_addr, size_t len);
+#endif
+
+/* nic3com.c */
+#if CONFIG_NIC3COM == 1
+int nic3com_init(void);
+extern const struct dev_entry nics_3com[];
+#endif
+
+/* gfxnvidia.c */
+#if CONFIG_GFXNVIDIA == 1
+int gfxnvidia_init(void);
+extern const struct dev_entry gfx_nvidia[];
+#endif
+
+/* drkaiser.c */
+#if CONFIG_DRKAISER == 1
+int drkaiser_init(void);
+extern const struct dev_entry drkaiser_pcidev[];
+#endif
+
+/* nicrealtek.c */
+#if CONFIG_NICREALTEK == 1
+int nicrealtek_init(void);
+extern const struct dev_entry nics_realtek[];
+#endif
+
+/* nicnatsemi.c */
+#if CONFIG_NICNATSEMI == 1
+int nicnatsemi_init(void);
+extern const struct dev_entry nics_natsemi[];
+#endif
+
+/* nicintel.c */
+#if CONFIG_NICINTEL == 1
+int nicintel_init(void);
+extern const struct dev_entry nics_intel[];
+#endif
+
+/* nicintel_spi.c */
+#if CONFIG_NICINTEL_SPI == 1
+int nicintel_spi_init(void);
+extern const struct dev_entry nics_intel_spi[];
+#endif
+
+/* nicintel_eeprom.c */
+#if CONFIG_NICINTEL_EEPROM == 1
+int nicintel_ee_init(void);
+extern const struct dev_entry nics_intel_ee[];
+#endif
+
+/* ogp_spi.c */
+#if CONFIG_OGP_SPI == 1
+int ogp_spi_init(void);
+extern const struct dev_entry ogp_spi[];
+#endif
+
+/* satamv.c */
+#if CONFIG_SATAMV == 1
+int satamv_init(void);
+extern const struct dev_entry satas_mv[];
+#endif
+
+/* satasii.c */
+#if CONFIG_SATASII == 1
+int satasii_init(void);
+extern const struct dev_entry satas_sii[];
+#endif
+
+/* atahpt.c */
+#if CONFIG_ATAHPT == 1
+int atahpt_init(void);
+extern const struct dev_entry ata_hpt[];
+#endif
+
+/* atavia.c */
+#if CONFIG_ATAVIA == 1
+int atavia_init(void);
+void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len);
+extern const struct dev_entry ata_via[];
+#endif
+
+/* atapromise.c */
+#if CONFIG_ATAPROMISE == 1
+int atapromise_init(void);
+void *atapromise_map(const char *descr, uintptr_t phys_addr, size_t len);
+extern const struct dev_entry ata_promise[];
+#endif
+
+/* it8212.c */
+#if CONFIG_IT8212 == 1
+int it8212_init(void);
+extern const struct dev_entry devs_it8212[];
+#endif
+
+/* ft2232_spi.c */
+#if CONFIG_FT2232_SPI == 1
+int ft2232_spi_init(void);
+extern const struct dev_entry devs_ft2232spi[];
+#endif
+
+/* usbblaster_spi.c */
+#if CONFIG_USBBLASTER_SPI == 1
+int usbblaster_spi_init(void);
+extern const struct dev_entry devs_usbblasterspi[];
+#endif
+
+/* mstarddc_spi.c */
+#if CONFIG_MSTARDDC_SPI == 1
+int mstarddc_spi_init(void);
+#endif
+
+/* pickit2_spi.c */
+#if CONFIG_PICKIT2_SPI == 1
+int pickit2_spi_init(void);
+extern const struct dev_entry devs_pickit2_spi[];
+#endif
+
+/* rayer_spi.c */
+#if CONFIG_RAYER_SPI == 1
+int rayer_spi_init(void);
+#endif
+
+/* pony_spi.c */
+#if CONFIG_PONY_SPI == 1
+int pony_spi_init(void);
+#endif
+
+/* bitbang_spi.c */
+int register_spi_bitbang_master(const struct bitbang_spi_master *master);
+
+/* buspirate_spi.c */
+#if CONFIG_BUSPIRATE_SPI == 1
+int buspirate_spi_init(void);
+#endif
+
+/* linux_spi.c */
+#if CONFIG_LINUX_SPI == 1
+int linux_spi_init(void);
+#endif
+
+/* dediprog.c */
+#if CONFIG_DEDIPROG == 1
+int dediprog_init(void);
+extern const struct dev_entry devs_dediprog[];
+#endif
+
+/* ch341a_spi.c */
+#if CONFIG_CH341A_SPI == 1
+int ch341a_spi_init(void);
+void ch341a_spi_delay(unsigned int usecs);
+extern const struct dev_entry devs_ch341a_spi[];
+#endif
+
+/* flashrom.c */
+struct decode_sizes {
+ uint32_t parallel;
+ uint32_t lpc;
+ uint32_t fwh;
+ uint32_t spi;
+};
+// FIXME: These need to be local, not global
+extern struct decode_sizes max_rom_decode;
+extern int programmer_may_write;
+extern unsigned long flashbase;
+unsigned int count_max_decode_exceedings(const struct flashctx *flash);
+char *extract_programmer_param(const char *param_name);
+
+/* spi.c */
+enum spi_controller {
+ SPI_CONTROLLER_NONE,
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+ SPI_CONTROLLER_ICH7,
+ SPI_CONTROLLER_ICH9,
+ SPI_CONTROLLER_IT85XX,
+ SPI_CONTROLLER_IT87XX,
+ SPI_CONTROLLER_SB600,
+ SPI_CONTROLLER_YANGTZE,
+ SPI_CONTROLLER_VIA,
+ SPI_CONTROLLER_WBSIO,
+#endif
+#endif
+#if CONFIG_FT2232_SPI == 1
+ SPI_CONTROLLER_FT2232,
+#endif
+#if CONFIG_DUMMY == 1
+ SPI_CONTROLLER_DUMMY,
+#endif
+#if CONFIG_BUSPIRATE_SPI == 1
+ SPI_CONTROLLER_BUSPIRATE,
+#endif
+#if CONFIG_DEDIPROG == 1
+ SPI_CONTROLLER_DEDIPROG,
+#endif
+#if CONFIG_OGP_SPI == 1 || CONFIG_NICINTEL_SPI == 1 || CONFIG_RAYER_SPI == 1 || CONFIG_PONY_SPI == 1 || (CONFIG_INTERNAL == 1 && (defined(__i386__) || defined(__x86_64__)))
+ SPI_CONTROLLER_BITBANG,
+#endif
+#if CONFIG_LINUX_SPI == 1
+ SPI_CONTROLLER_LINUX,
+#endif
+#if CONFIG_SERPROG == 1
+ SPI_CONTROLLER_SERPROG,
+#endif
+#if CONFIG_USBBLASTER_SPI == 1
+ SPI_CONTROLLER_USBBLASTER,
+#endif
+#if CONFIG_MSTARDDC_SPI == 1
+ SPI_CONTROLLER_MSTARDDC,
+#endif
+#if CONFIG_PICKIT2_SPI == 1
+ SPI_CONTROLLER_PICKIT2,
+#endif
+#if CONFIG_CH341A_SPI == 1
+ SPI_CONTROLLER_CH341A_SPI,
+#endif
+};
+
+#define MAX_DATA_UNSPECIFIED 0
+#define MAX_DATA_READ_UNLIMITED 64 * 1024
+#define MAX_DATA_WRITE_UNLIMITED 256
+struct spi_master {
+ enum spi_controller type;
+ unsigned int max_data_read; // (Ideally,) maximum data read size in one go (excluding opcode+address).
+ unsigned int max_data_write; // (Ideally,) maximum data write size in one go (excluding opcode+address).
+ int (*command)(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+ int (*multicommand)(struct flashctx *flash, struct spi_command *cmds);
+
+ /* Optimized functions for this master */
+ int (*read)(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+ int (*write_256)(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+ int (*write_aai)(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+ const void *data;
+};
+
+int default_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+int default_spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds);
+int default_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+int default_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int default_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+int register_spi_master(const struct spi_master *mst);
+
+/* The following enum is needed by ich_descriptor_tool and ich* code as well as in chipset_enable.c. */
+enum ich_chipset {
+ CHIPSET_ICH_UNKNOWN,
+ CHIPSET_ICH,
+ CHIPSET_ICH2345,
+ CHIPSET_ICH6,
+ CHIPSET_POULSBO, /* SCH U* */
+ CHIPSET_TUNNEL_CREEK, /* Atom E6xx */
+ CHIPSET_CENTERTON, /* Atom S1220 S1240 S1260 */
+ CHIPSET_ICH7,
+ CHIPSET_ICH8,
+ CHIPSET_ICH9,
+ CHIPSET_ICH10,
+ CHIPSET_5_SERIES_IBEX_PEAK,
+ CHIPSET_6_SERIES_COUGAR_POINT,
+ CHIPSET_7_SERIES_PANTHER_POINT,
+ CHIPSET_8_SERIES_LYNX_POINT,
+ CHIPSET_BAYTRAIL, /* Actually all with Silvermont architecture: Bay Trail, Avoton/Rangeley */
+ CHIPSET_8_SERIES_LYNX_POINT_LP,
+ CHIPSET_8_SERIES_WELLSBURG,
+ CHIPSET_9_SERIES_WILDCAT_POINT,
+};
+
+/* ichspi.c */
+#if CONFIG_INTERNAL == 1
+extern uint32_t ichspi_bbar;
+int ich_init_spi(struct pci_dev *dev, void *spibar, enum ich_chipset ich_generation);
+int via_init_spi(struct pci_dev *dev, uint32_t mmio_base);
+
+/* amd_imc.c */
+int amd_imc_shutdown(struct pci_dev *dev);
+
+/* it85spi.c */
+int it85xx_spi_init(struct superio s);
+
+/* it87spi.c */
+void enter_conf_mode_ite(uint16_t port);
+void exit_conf_mode_ite(uint16_t port);
+void probe_superio_ite(void);
+int init_superio_ite(void);
+
+/* mcp6x_spi.c */
+int mcp6x_spi_init(int want_spi);
+
+/* sb600spi.c */
+int sb600_probe_spi(struct pci_dev *dev);
+
+/* wbsio_spi.c */
+int wbsio_check_for_spi(void);
+#endif
+
+/* opaque.c */
+struct opaque_master {
+ int max_data_read;
+ int max_data_write;
+ /* Specific functions for this master */
+ int (*probe) (struct flashctx *flash);
+ int (*read) (struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+ int (*write) (struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len);
+ int (*erase) (struct flashctx *flash, unsigned int blockaddr, unsigned int blocklen);
+ const void *data;
+};
+int register_opaque_master(const struct opaque_master *mst);
+
+/* programmer.c */
+int noop_shutdown(void);
+void *fallback_map(const char *descr, uintptr_t phys_addr, size_t len);
+void fallback_unmap(void *virt_addr, size_t len);
+void noop_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+void fallback_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr);
+void fallback_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr);
+void fallback_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len);
+uint16_t fallback_chip_readw(const struct flashctx *flash, const chipaddr addr);
+uint32_t fallback_chip_readl(const struct flashctx *flash, const chipaddr addr);
+void fallback_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len);
+struct par_master {
+ void (*chip_writeb) (const struct flashctx *flash, uint8_t val, chipaddr addr);
+ void (*chip_writew) (const struct flashctx *flash, uint16_t val, chipaddr addr);
+ void (*chip_writel) (const struct flashctx *flash, uint32_t val, chipaddr addr);
+ void (*chip_writen) (const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len);
+ uint8_t (*chip_readb) (const struct flashctx *flash, const chipaddr addr);
+ uint16_t (*chip_readw) (const struct flashctx *flash, const chipaddr addr);
+ uint32_t (*chip_readl) (const struct flashctx *flash, const chipaddr addr);
+ void (*chip_readn) (const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len);
+ const void *data;
+};
+int register_par_master(const struct par_master *mst, const enum chipbustype buses);
+struct registered_master {
+ enum chipbustype buses_supported;
+ union {
+ struct par_master par;
+ struct spi_master spi;
+ struct opaque_master opaque;
+ };
+};
+extern struct registered_master registered_masters[];
+extern int registered_master_count;
+int register_master(const struct registered_master *mst);
+
+/* serprog.c */
+#if CONFIG_SERPROG == 1
+int serprog_init(void);
+void serprog_delay(unsigned int usecs);
+void *serprog_map(const char *descr, uintptr_t phys_addr, size_t len);
+#endif
+
+/* serial.c */
+#if IS_WINDOWS
+typedef HANDLE fdtype;
+#define SER_INV_FD INVALID_HANDLE_VALUE
+#else
+typedef int fdtype;
+#define SER_INV_FD -1
+#endif
+
+void sp_flush_incoming(void);
+fdtype sp_openserport(char *dev, int baud);
+extern fdtype sp_fd;
+int serialport_shutdown(void *data);
+int serialport_write(const unsigned char *buf, unsigned int writecnt);
+int serialport_write_nonblock(const unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote);
+int serialport_read(unsigned char *buf, unsigned int readcnt);
+int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read);
+
+/* Serial port/pin mapping:
+
+ 1 CD <-
+ 2 RXD <-
+ 3 TXD ->
+ 4 DTR ->
+ 5 GND --
+ 6 DSR <-
+ 7 RTS ->
+ 8 CTS <-
+ 9 RI <-
+*/
+enum SP_PIN {
+ PIN_CD = 1,
+ PIN_RXD,
+ PIN_TXD,
+ PIN_DTR,
+ PIN_GND,
+ PIN_DSR,
+ PIN_RTS,
+ PIN_CTS,
+ PIN_RI,
+};
+
+void sp_set_pin(enum SP_PIN pin, int val);
+int sp_get_pin(enum SP_PIN pin);
+
+#endif /* !__PROGRAMMER_H__ */
diff --git a/rayer_spi.c b/rayer_spi.c
new file mode 100644
index 0000000..3656d26
--- /dev/null
+++ b/rayer_spi.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Driver for various LPT adapters.
+ *
+ * This driver uses non-portable direct I/O port accesses which won't work on
+ * any non-x86 platform, and even on x86 there is a high chance there will be
+ * collisions with any loaded parallel port drivers.
+ * The big advantage of direct port I/O is OS independence and speed because
+ * most OS parport drivers will perform many unnecessary accesses although
+ * this driver just treats the parallel port as a GPIO set.
+ */
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+/* We have two sets of pins, out and in. The numbers for both sets are
+ * independent and are bitshift values, not real pin numbers.
+ * Default settings are for the RayeR hardware.
+ */
+
+struct rayer_programmer {
+ const char *type;
+ const enum test_state status;
+ const char *description;
+ const void *dev_data;
+};
+
+struct rayer_pinout {
+ uint8_t cs_bit;
+ uint8_t sck_bit;
+ uint8_t mosi_bit;
+ uint8_t miso_bit;
+ void (*preinit)(const void *);
+ int (*shutdown)(void *);
+};
+
+static const struct rayer_pinout rayer_spipgm = {
+ .cs_bit = 5,
+ .sck_bit = 6,
+ .mosi_bit = 7,
+ .miso_bit = 6,
+};
+
+static void dlc5_preinit(const void *);
+static int dlc5_shutdown(void *);
+
+static const struct rayer_pinout xilinx_dlc5 = {
+ .cs_bit = 2,
+ .sck_bit = 1,
+ .mosi_bit = 0,
+ .miso_bit = 4,
+ .preinit = dlc5_preinit,
+ .shutdown = dlc5_shutdown,
+};
+
+static void byteblaster_preinit(const void *);
+static int byteblaster_shutdown(void *);
+
+static const struct rayer_pinout altera_byteblastermv = {
+ .cs_bit = 1,
+ .sck_bit = 0,
+ .mosi_bit = 6,
+ .miso_bit = 7,
+ .preinit = byteblaster_preinit,
+ .shutdown = byteblaster_shutdown,
+};
+
+static void stk200_preinit(const void *);
+static int stk200_shutdown(void *);
+
+static const struct rayer_pinout atmel_stk200 = {
+ .cs_bit = 7,
+ .sck_bit = 4,
+ .mosi_bit = 5,
+ .miso_bit = 6,
+ .preinit = stk200_preinit,
+ .shutdown = stk200_shutdown,
+};
+
+static const struct rayer_pinout wiggler_lpt = {
+ .cs_bit = 1,
+ .sck_bit = 2,
+ .mosi_bit = 3,
+ .miso_bit = 7,
+};
+
+static const struct rayer_pinout spi_tt = {
+ .cs_bit = 2,
+ .sck_bit = 0,
+ .mosi_bit = 4,
+ .miso_bit = 7,
+};
+
+static const struct rayer_programmer rayer_spi_types[] = {
+ {"rayer", NT, "RayeR SPIPGM", &rayer_spipgm},
+ {"xilinx", NT, "Xilinx Parallel Cable III (DLC 5)", &xilinx_dlc5},
+ {"byteblastermv", OK, "Altera ByteBlasterMV", &altera_byteblastermv},
+ {"stk200", NT, "Atmel STK200/300 adapter", &atmel_stk200},
+ {"wiggler", OK, "Wiggler LPT", &wiggler_lpt},
+ {"spi_tt", NT, "SPI Tiny Tools (SPI_TT LPT)", &spi_tt},
+ {0},
+};
+
+static const struct rayer_pinout *pinout = NULL;
+
+static uint16_t lpt_iobase;
+
+/* Cached value of last byte sent. */
+static uint8_t lpt_outbyte;
+
+static void rayer_bitbang_set_cs(int val)
+{
+ lpt_outbyte &= ~(1 << pinout->cs_bit);
+ lpt_outbyte |= (val << pinout->cs_bit);
+ OUTB(lpt_outbyte, lpt_iobase);
+}
+
+static void rayer_bitbang_set_sck(int val)
+{
+ lpt_outbyte &= ~(1 << pinout->sck_bit);
+ lpt_outbyte |= (val << pinout->sck_bit);
+ OUTB(lpt_outbyte, lpt_iobase);
+}
+
+static void rayer_bitbang_set_mosi(int val)
+{
+ lpt_outbyte &= ~(1 << pinout->mosi_bit);
+ lpt_outbyte |= (val << pinout->mosi_bit);
+ OUTB(lpt_outbyte, lpt_iobase);
+}
+
+static int rayer_bitbang_get_miso(void)
+{
+ uint8_t tmp;
+
+ tmp = INB(lpt_iobase + 1) ^ 0x80; // bit.7 inverted
+ tmp = (tmp >> pinout->miso_bit) & 0x1;
+ return tmp;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_rayer = {
+ .type = BITBANG_SPI_MASTER_RAYER,
+ .set_cs = rayer_bitbang_set_cs,
+ .set_sck = rayer_bitbang_set_sck,
+ .set_mosi = rayer_bitbang_set_mosi,
+ .get_miso = rayer_bitbang_get_miso,
+ .half_period = 0,
+};
+
+int rayer_spi_init(void)
+{
+ const struct rayer_programmer *prog = rayer_spi_types;
+ char *arg = NULL;
+
+ /* Non-default port requested? */
+ arg = extract_programmer_param("iobase");
+ if (arg) {
+ char *endptr = NULL;
+ unsigned long tmp;
+ tmp = strtoul(arg, &endptr, 0);
+ /* Port 0, port >0x10000, unaligned ports and garbage strings
+ * are rejected.
+ */
+ if (!tmp || (tmp >= 0x10000) || (tmp & 0x3) ||
+ (*endptr != '\0')) {
+ /* Using ports below 0x100 is a really bad idea, and
+ * should only be done if no port between 0x100 and
+ * 0xfffc works due to routing issues.
+ */
+ msg_perr("Error: iobase= specified, but the I/O base "
+ "given was invalid.\nIt must be a multiple of "
+ "0x4 and lie between 0x100 and 0xfffc.\n");
+ free(arg);
+ return 1;
+ } else {
+ lpt_iobase = (uint16_t)tmp;
+ msg_pinfo("Non-default I/O base requested. This will "
+ "not change the hardware settings.\n");
+ }
+ } else {
+ /* Pick a default value for the I/O base. */
+ lpt_iobase = 0x378;
+ }
+ free(arg);
+
+ msg_pdbg("Using address 0x%x as I/O base for parallel port access.\n",
+ lpt_iobase);
+
+ arg = extract_programmer_param("type");
+ if (arg) {
+ for (; prog->type != NULL; prog++) {
+ if (strcasecmp(arg, prog->type) == 0) {
+ break;
+ }
+ }
+ if (prog->type == NULL) {
+ msg_perr("Error: Invalid device type specified.\n");
+ free(arg);
+ return 1;
+ }
+ free(arg);
+ }
+ msg_pinfo("Using %s pinout.\n", prog->description);
+ pinout = (struct rayer_pinout *)prog->dev_data;
+
+ if (rget_io_perms())
+ return 1;
+
+ /* Get the initial value before writing to any line. */
+ lpt_outbyte = INB(lpt_iobase);
+
+ if (pinout->shutdown)
+ register_shutdown(pinout->shutdown, (void*)pinout);
+ if (pinout->preinit)
+ pinout->preinit(pinout);
+
+ if (register_spi_bitbang_master(&bitbang_spi_master_rayer))
+ return 1;
+
+ return 0;
+}
+
+static void byteblaster_preinit(const void *data){
+ msg_pdbg("byteblaster_preinit\n");
+ /* Assert #EN signal. */
+ OUTB(2, lpt_iobase + 2 );
+}
+
+static int byteblaster_shutdown(void *data){
+ msg_pdbg("byteblaster_shutdown\n");
+ /* De-Assert #EN signal. */
+ OUTB(0, lpt_iobase + 2 );
+ return 0;
+}
+
+static void stk200_preinit(const void *data) {
+ msg_pdbg("stk200_init\n");
+ /* Assert #EN signals, set LED signal. */
+ lpt_outbyte = (1 << 6) ;
+ OUTB(lpt_outbyte, lpt_iobase);
+}
+
+static int stk200_shutdown(void *data) {
+ msg_pdbg("stk200_shutdown\n");
+ /* Assert #EN signals, clear LED signal. */
+ lpt_outbyte = (1 << 2) | (1 << 3);
+ OUTB(lpt_outbyte, lpt_iobase);
+ return 0;
+}
+
+static void dlc5_preinit(const void *data) {
+ msg_pdbg("dlc5_preinit\n");
+ /* Assert pin 6 to receive MISO. */
+ lpt_outbyte |= (1<<4);
+ OUTB(lpt_outbyte, lpt_iobase);
+}
+
+static int dlc5_shutdown(void *data) {
+ msg_pdbg("dlc5_shutdown\n");
+ /* De-assert pin 6 to force MISO low. */
+ lpt_outbyte &= ~(1<<4);
+ OUTB(lpt_outbyte, lpt_iobase);
+ return 0;
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/satamv.c b/satamv.c
new file mode 100644
index 0000000..e76d126
--- /dev/null
+++ b/satamv.c
@@ -0,0 +1,197 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010,2011 Carl-Daniel Hailfinger
+ * Written by Carl-Daniel Hailfinger for Angelbird Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Datasheets are not public (yet?) */
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+uint8_t *mv_bar;
+uint16_t mv_iobar;
+
+const struct dev_entry satas_mv[] = {
+ /* 88SX6041 and 88SX6042 are the same according to the datasheet. */
+ {0x11ab, 0x7042, OK, "Marvell", "88SX7042 PCI-e 4-port SATA-II"},
+
+ {0},
+};
+
+#define NVRAM_PARAM 0x1045c
+#define FLASH_PARAM 0x1046c
+#define EXPANSION_ROM_BAR_CONTROL 0x00d2c
+#define PCI_BAR2_CONTROL 0x00c08
+#define GPIO_PORT_CONTROL 0x104f0
+
+static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t satamv_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static const struct par_master par_master_satamv = {
+ .chip_readb = satamv_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = satamv_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+/*
+ * Random notes:
+ * FCE# Flash Chip Enable
+ * FWE# Flash Write Enable
+ * FOE# Flash Output Enable
+ * FALE[1:0] Flash Address Latch Enable
+ * FAD[7:0] Flash Multiplexed Address/Data Bus
+ * FA[2:0] Flash Address Low
+ *
+ * GPIO[15,2] GPIO Port Mode
+ * GPIO[4:3] Flash Size
+ *
+ * 0xd2c Expansion ROM BAR Control
+ * 0xc08 PCI BAR2 (Flash/NVRAM) Control
+ * 0x1046c Flash Parameters
+ */
+int satamv_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uintptr_t addr;
+ uint32_t tmp;
+
+ if (rget_io_perms())
+ return 1;
+
+ /* BAR0 has all internal registers memory mapped. */
+ dev = pcidev_init(satas_mv, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!addr)
+ return 1;
+
+ mv_bar = rphysmap("Marvell 88SX7042 registers", addr, 0x20000);
+ if (mv_bar == ERROR_PTR)
+ return 1;
+
+ tmp = pci_mmio_readl(mv_bar + FLASH_PARAM);
+ msg_pspew("Flash Parameters:\n");
+ msg_pspew("TurnOff=0x%01x\n", (tmp >> 0) & 0x7);
+ msg_pspew("Acc2First=0x%01x\n", (tmp >> 3) & 0xf);
+ msg_pspew("Acc2Next=0x%01x\n", (tmp >> 7) & 0xf);
+ msg_pspew("ALE2Wr=0x%01x\n", (tmp >> 11) & 0x7);
+ msg_pspew("WrLow=0x%01x\n", (tmp >> 14) & 0x7);
+ msg_pspew("WrHigh=0x%01x\n", (tmp >> 17) & 0x7);
+ msg_pspew("Reserved[21:20]=0x%01x\n", (tmp >> 20) & 0x3);
+ msg_pspew("TurnOffExt=0x%01x\n", (tmp >> 22) & 0x1);
+ msg_pspew("Acc2FirstExt=0x%01x\n", (tmp >> 23) & 0x1);
+ msg_pspew("Acc2NextExt=0x%01x\n", (tmp >> 24) & 0x1);
+ msg_pspew("ALE2WrExt=0x%01x\n", (tmp >> 25) & 0x1);
+ msg_pspew("WrLowExt=0x%01x\n", (tmp >> 26) & 0x1);
+ msg_pspew("WrHighExt=0x%01x\n", (tmp >> 27) & 0x1);
+ msg_pspew("Reserved[31:28]=0x%01x\n", (tmp >> 28) & 0xf);
+
+ tmp = pci_mmio_readl(mv_bar + EXPANSION_ROM_BAR_CONTROL);
+ msg_pspew("Expansion ROM BAR Control:\n");
+ msg_pspew("ExpROMSz=0x%01x\n", (tmp >> 19) & 0x7);
+
+ /* Enable BAR2 mapping to flash */
+ tmp = pci_mmio_readl(mv_bar + PCI_BAR2_CONTROL);
+ msg_pspew("PCI BAR2 (Flash/NVRAM) Control:\n");
+ msg_pspew("Bar2En=0x%01x\n", (tmp >> 0) & 0x1);
+ msg_pspew("BAR2TransAttr=0x%01x\n", (tmp >> 1) & 0x1f);
+ msg_pspew("BAR2Sz=0x%01x\n", (tmp >> 19) & 0x7);
+ tmp &= 0xffffffc0;
+ tmp |= 0x0000001f;
+ pci_rmmio_writel(tmp, mv_bar + PCI_BAR2_CONTROL);
+
+ /* Enable flash: GPIO Port Control Register 0x104f0 */
+ tmp = pci_mmio_readl(mv_bar + GPIO_PORT_CONTROL);
+ msg_pspew("GPIOPortMode=0x%01x\n", (tmp >> 0) & 0x3);
+ if (((tmp >> 0) & 0x3) != 0x2)
+ msg_pinfo("Warning! Either the straps are incorrect or you "
+ "have no flash or someone overwrote the strap "
+ "values!\n");
+ tmp &= 0xfffffffc;
+ tmp |= 0x2;
+ pci_rmmio_writel(tmp, mv_bar + GPIO_PORT_CONTROL);
+
+ /* Get I/O BAR location. */
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2);
+ if (!addr)
+ return 1;
+
+ /* Truncate to reachable range.
+ * FIXME: Check if the I/O BAR is actually reachable.
+ * This is an arch specific check.
+ */
+ mv_iobar = addr & 0xffff;
+ msg_pspew("Activating I/O BAR at 0x%04x\n", mv_iobar);
+
+ /* 512 kByte with two 8-bit latches, and
+ * 4 MByte with additional 3-bit latch. */
+ max_rom_decode.parallel = 4 * 1024 * 1024;
+ register_par_master(&par_master_satamv, BUS_PARALLEL);
+
+ return 0;
+}
+
+/* BAR2 (MEM) can map NVRAM and flash. We set it to flash in the init function.
+ * If BAR2 is disabled, it still can be accessed indirectly via BAR1 (I/O).
+ * This code only supports indirect accesses for now.
+ */
+
+/* Indirect access to via the I/O BAR1. */
+static void satamv_indirect_chip_writeb(uint8_t val, chipaddr addr)
+{
+ /* 0x80000000 selects BAR2 for remapping. */
+ OUTL(((uint32_t)addr | 0x80000000) & 0xfffffffc, mv_iobar);
+ OUTB(val, mv_iobar + 0x80 + (addr & 0x3));
+}
+
+/* Indirect access to via the I/O BAR1. */
+static uint8_t satamv_indirect_chip_readb(const chipaddr addr)
+{
+ /* 0x80000000 selects BAR2 for remapping. */
+ OUTL(((uint32_t)addr | 0x80000000) & 0xfffffffc, mv_iobar);
+ return INB(mv_iobar + 0x80 + (addr & 0x3));
+}
+
+/* FIXME: Prefer direct access to BAR2 if BAR2 is active. */
+static void satamv_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ satamv_indirect_chip_writeb(val, addr);
+}
+
+/* FIXME: Prefer direct access to BAR2 if BAR2 is active. */
+static uint8_t satamv_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ return satamv_indirect_chip_readb(addr);
+}
+
+#else
+#error PCI port I/O access is not supported on this architecture yet.
+#endif
diff --git a/satasii.c b/satasii.c
new file mode 100644
index 0000000..368d7d4
--- /dev/null
+++ b/satasii.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Datasheets can be found on http://www.siliconimage.com. Great thanks! */
+
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_SII 0x1095
+
+#define SATASII_MEMMAP_SIZE 0x100
+
+static uint8_t *sii_bar;
+static uint16_t id;
+
+const struct dev_entry satas_sii[] = {
+ {0x1095, 0x0680, OK, "Silicon Image", "PCI0680 Ultra ATA-133 Host Ctrl"},
+ {0x1095, 0x3112, OK, "Silicon Image", "SiI 3112 [SATALink/SATARaid] SATA Ctrl"},
+ {0x1095, 0x3114, OK, "Silicon Image", "SiI 3114 [SATALink/SATARaid] SATA Ctrl"},
+ {0x1095, 0x3124, OK, "Silicon Image", "SiI 3124 PCI-X SATA Ctrl"},
+ {0x1095, 0x3132, OK, "Silicon Image", "SiI 3132 SATA Raid II Ctrl"},
+ {0x1095, 0x3512, OK, "Silicon Image", "SiI 3512 [SATALink/SATARaid] SATA Ctrl"},
+
+ {0},
+};
+
+static void satasii_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+static uint8_t satasii_chip_readb(const struct flashctx *flash, const chipaddr addr);
+static const struct par_master par_master_satasii = {
+ .chip_readb = satasii_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = fallback_chip_readn,
+ .chip_writeb = satasii_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+static uint32_t satasii_wait_done(void)
+{
+ uint32_t ctrl_reg;
+ int i = 0;
+ while ((ctrl_reg = pci_mmio_readl(sii_bar)) & (1 << 25)) {
+ if (++i > 10000) {
+ msg_perr("%s: control register stuck at %08x, ignoring.\n",
+ __func__, pci_mmio_readl(sii_bar));
+ break;
+ }
+ }
+ return ctrl_reg;
+}
+
+int satasii_init(void)
+{
+ struct pci_dev *dev = NULL;
+ uint32_t addr;
+ uint16_t reg_offset;
+
+ if (rget_io_perms())
+ return 1;
+
+ dev = pcidev_init(satas_sii, PCI_BASE_ADDRESS_0);
+ if (!dev)
+ return 1;
+
+ id = dev->device_id;
+
+ if ((id == 0x3132) || (id == 0x3124)) {
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
+ if (!addr)
+ return 1;
+ reg_offset = 0x70;
+ } else {
+ addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_5);
+ if (!addr)
+ return 1;
+ reg_offset = 0x50;
+ }
+
+ sii_bar = rphysmap("SATA SiI registers", addr, SATASII_MEMMAP_SIZE);
+ if (sii_bar == ERROR_PTR)
+ return 1;
+ sii_bar += reg_offset;
+
+ /* Check if ROM cycle are OK. */
+ if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26))))
+ msg_pwarn("Warning: Flash seems unconnected.\n");
+
+ register_par_master(&par_master_satasii, BUS_PARALLEL);
+
+ return 0;
+}
+
+static void satasii_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr)
+{
+ uint32_t data_reg;
+ uint32_t ctrl_reg = satasii_wait_done();
+
+ /* Mask out unused/reserved bits, set writes and start transaction. */
+ ctrl_reg &= 0xfcf80000;
+ ctrl_reg |= (1 << 25) | (0 << 24) | ((uint32_t) addr & 0x7ffff);
+
+ data_reg = (pci_mmio_readl((sii_bar + 4)) & ~0xff) | val;
+ pci_mmio_writel(data_reg, (sii_bar + 4));
+ pci_mmio_writel(ctrl_reg, sii_bar);
+
+ satasii_wait_done();
+}
+
+static uint8_t satasii_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+ uint32_t ctrl_reg = satasii_wait_done();
+
+ /* Mask out unused/reserved bits, set reads and start transaction. */
+ ctrl_reg &= 0xfcf80000;
+ ctrl_reg |= (1 << 25) | (1 << 24) | ((uint32_t) addr & 0x7ffff);
+
+ pci_mmio_writel(ctrl_reg, sii_bar);
+
+ satasii_wait_done();
+
+ return (pci_mmio_readl(sii_bar + 4)) & 0xff;
+}
diff --git a/sb600spi.c b/sb600spi.c
new file mode 100644
index 0000000..6bd5679
--- /dev/null
+++ b/sb600spi.c
@@ -0,0 +1,700 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 Wang Qingpei <Qingpei.Wang@amd.com>
+ * Copyright (C) 2008 Joe Bao <Zheng.Bao@amd.com>
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ * Copyright (C) 2009, 2010, 2013 Carl-Daniel Hailfinger
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+
+/* This struct is unused, but helps visualize the SB600 SPI BAR layout.
+ *struct sb600_spi_controller {
+ * unsigned int spi_cntrl0; / * 00h * /
+ * unsigned int restrictedcmd1; / * 04h * /
+ * unsigned int restrictedcmd2; / * 08h * /
+ * unsigned int spi_cntrl1; / * 0ch * /
+ * unsigned int spi_cmdvalue0; / * 10h * /
+ * unsigned int spi_cmdvalue1; / * 14h * /
+ * unsigned int spi_cmdvalue2; / * 18h * /
+ * unsigned int spi_fakeid; / * 1Ch * /
+ *};
+ */
+
+static uint8_t *sb600_spibar = NULL;
+enum amd_chipset {
+ CHIPSET_AMD_UNKNOWN,
+ CHIPSET_SB6XX,
+ CHIPSET_SB7XX, /* SP5100 too */
+ CHIPSET_SB89XX, /* Hudson-1 too */
+ CHIPSET_HUDSON234,
+ CHIPSET_BOLTON,
+ CHIPSET_YANGTZE,
+};
+static enum amd_chipset amd_gen = CHIPSET_AMD_UNKNOWN;
+
+#define FIFO_SIZE_OLD 8
+#define FIFO_SIZE_YANGTZE 71
+
+static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+static int spi100_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr);
+
+static struct spi_master spi_master_sb600 = {
+ .type = SPI_CONTROLLER_SB600,
+ .max_data_read = FIFO_SIZE_OLD,
+ .max_data_write = FIFO_SIZE_OLD - 3,
+ .command = sb600_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static struct spi_master spi_master_yangtze = {
+ .type = SPI_CONTROLLER_YANGTZE,
+ .max_data_read = FIFO_SIZE_YANGTZE - 3, /* Apparently the big SPI 100 buffer is not a ring buffer. */
+ .max_data_write = FIFO_SIZE_YANGTZE - 3,
+ .command = spi100_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static void determine_generation(struct pci_dev *dev)
+{
+ amd_gen = CHIPSET_AMD_UNKNOWN;
+ msg_pdbg2("Trying to determine the generation of the SPI interface... ");
+ if (dev->device_id == 0x438d) {
+ amd_gen = CHIPSET_SB6XX;
+ msg_pdbg("SB6xx detected.\n");
+ } else if (dev->device_id == 0x439d) {
+ struct pci_dev *smbus_dev = pci_dev_find(0x1002, 0x4385);
+ if (smbus_dev == NULL)
+ return;
+ uint8_t rev = pci_read_byte(smbus_dev, PCI_REVISION_ID);
+ if (rev >= 0x39 && rev <= 0x3D) {
+ amd_gen = CHIPSET_SB7XX;
+ msg_pdbg("SB7xx/SP5100 detected.\n");
+ } else if (rev >= 0x40 && rev <= 0x42) {
+ amd_gen = CHIPSET_SB89XX;
+ msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n");
+ } else {
+ msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n"
+ "Assuming SB8xx/SB9xx/Hudson-1. Please send a log to flashrom@flashrom.org\n",
+ rev);
+ amd_gen = CHIPSET_SB89XX;
+ }
+ } else if (dev->device_id == 0x780e) {
+ /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash)
+ * although they use different SPI interfaces. */
+#ifdef USE_YANGTZE_HEURISTICS
+ /* This heuristic accesses the SPI interface MMIO BAR at locations beyond those supported by
+ * Hudson in the hope of getting 0xff readback on older chipsets and non-0xff readback on
+ * Yangtze (and newer, compatible chipsets). */
+ int i;
+ msg_pdbg("Checking for AMD Yangtze (Kabini/Temash) or later... ");
+ for (i = 0x20; i <= 0x4f; i++) {
+ if (mmio_readb(sb600_spibar + i) != 0xff) {
+ amd_gen = CHIPSET_YANGTZE;
+ msg_pdbg("found.\n");
+ return;
+ }
+ }
+ msg_pdbg("not found. Assuming Hudson.\n");
+ amd_gen = CHIPSET_HUDSON234;
+#else
+ struct pci_dev *smbus_dev = pci_dev_find(0x1022, 0x780B);
+ if (smbus_dev == NULL) {
+ msg_pdbg("No SMBus device with ID 1022:780B found.\n");
+ return;
+ }
+ uint8_t rev = pci_read_byte(smbus_dev, PCI_REVISION_ID);
+ if (rev >= 0x11 && rev <= 0x15) {
+ amd_gen = CHIPSET_HUDSON234;
+ msg_pdbg("Hudson-2/3/4 detected.\n");
+ } else if (rev == 0x16) {
+ amd_gen = CHIPSET_BOLTON;
+ msg_pdbg("Bolton detected.\n");
+ } else if ((rev >= 0x39 && rev <= 0x3A) || rev == 0x42) {
+ amd_gen = CHIPSET_YANGTZE;
+ msg_pdbg("Yangtze detected.\n");
+ } else {
+ msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
+ "Please report this to flashrom@flashrom.org and include this log and\n"
+ "the output of lspci -nnvx, thanks!.\n", rev);
+ }
+#endif
+ } else
+ msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n"
+ "Please report this to flashrom@flashrom.org and include this log and\n"
+ "the output of lspci -nnvx, thanks!\n",
+ __func__, dev->vendor_id, dev->device_id);
+}
+
+static void reset_internal_fifo_pointer(void)
+{
+ mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
+
+ /* FIXME: This loop needs a timeout and a clearer message. */
+ while (mmio_readb(sb600_spibar + 0xD) & 0x7)
+ msg_pspew("reset\n");
+}
+
+static int compare_internal_fifo_pointer(uint8_t want)
+{
+ uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07;
+ want %= FIFO_SIZE_OLD;
+ if (have != want) {
+ msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want);
+ msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
+ "Please stop all applications and drivers and IPMI which access the flash chip.\n");
+ return 1;
+ } else {
+ msg_pspew("AMD SPI FIFO pointer is %d, wanted %d\n", have, want);
+ return 0;
+ }
+}
+
+/* Check the number of bytes to be transmitted and extract opcode. */
+static int check_readwritecnt(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt)
+{
+ unsigned int maxwritecnt = flash->mst->spi.max_data_write + 3;
+ if (writecnt > maxwritecnt) {
+ msg_pinfo("%s: SPI controller can not send %d bytes, it is limited to %d bytes\n",
+ __func__, writecnt, maxwritecnt);
+ return SPI_INVALID_LENGTH;
+ }
+
+ unsigned int maxreadcnt = flash->mst->spi.max_data_read;
+ if (readcnt > maxreadcnt) {
+ msg_pinfo("%s: SPI controller can not receive %d bytes, it is limited to %d bytes\n",
+ __func__, readcnt, maxreadcnt);
+ return SPI_INVALID_LENGTH;
+ }
+ return 0;
+}
+
+static void execute_command(void)
+{
+ msg_pspew("Executing... ");
+ mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
+ while (mmio_readb(sb600_spibar + 2) & 1)
+ ;
+ msg_pspew("done\n");
+}
+
+static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ /* First byte is cmd which can not be sent through the FIFO. */
+ unsigned char cmd = *writearr++;
+ writecnt--;
+ msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
+ mmio_writeb(cmd, sb600_spibar + 0);
+
+ int ret = check_readwritecnt(flash, writecnt, readcnt);
+ if (ret != 0)
+ return ret;
+
+ /* This is a workaround for a bug in SPI controller. If we only send
+ * an opcode and no additional data/address, the SPI controller will
+ * read one byte too few from the chip. Basically, the last byte of
+ * the chip response is discarded and will not end up in the FIFO.
+ * It is unclear if the CS# line is set high too early as well.
+ */
+ unsigned int readoffby1 = (writecnt > 0) ? 0 : 1;
+ uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt);
+ mmio_writeb(readwrite, sb600_spibar + 1);
+
+ reset_internal_fifo_pointer();
+ msg_pspew("Filling FIFO: ");
+ int count;
+ for (count = 0; count < writecnt; count++) {
+ msg_pspew("[%02x]", writearr[count]);
+ mmio_writeb(writearr[count], sb600_spibar + 0xC);
+ }
+ msg_pspew("\n");
+ if (compare_internal_fifo_pointer(writecnt))
+ return SPI_PROGRAMMER_ERROR;
+
+ /*
+ * We should send the data in sequence, which means we need to reset
+ * the FIFO pointer to the first byte we want to send.
+ */
+ reset_internal_fifo_pointer();
+ execute_command();
+ if (compare_internal_fifo_pointer(writecnt + readcnt))
+ return SPI_PROGRAMMER_ERROR;
+
+ /*
+ * After the command executed, we should find out the index of the
+ * received byte. Here we just reset the FIFO pointer and skip the
+ * writecnt.
+ * It would be possible to increase the FIFO pointer by one instead
+ * of reading and discarding one byte from the FIFO.
+ * The FIFO is implemented on top of an 8 byte ring buffer and the
+ * buffer is never cleared. For every byte that is shifted out after
+ * the opcode, the FIFO already stores the response from the chip.
+ * Usually, the chip will respond with 0x00 or 0xff.
+ */
+ reset_internal_fifo_pointer();
+
+ /* Skip the bytes we sent. */
+ msg_pspew("Skipping: ");
+ for (count = 0; count < writecnt; count++) {
+ msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC));
+ }
+ msg_pspew("\n");
+ if (compare_internal_fifo_pointer(writecnt))
+ return SPI_PROGRAMMER_ERROR;
+
+ msg_pspew("Reading FIFO: ");
+ for (count = 0; count < readcnt; count++) {
+ readarr[count] = mmio_readb(sb600_spibar + 0xC);
+ msg_pspew("[%02x]", readarr[count]);
+ }
+ msg_pspew("\n");
+ if (compare_internal_fifo_pointer(writecnt+readcnt))
+ return SPI_PROGRAMMER_ERROR;
+
+ if (mmio_readb(sb600_spibar + 1) != readwrite) {
+ msg_perr("Unexpected change in AMD SPI read/write count!\n");
+ msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
+ "Please stop all applications and drivers and IPMI which access the flash chip.\n");
+ return SPI_PROGRAMMER_ERROR;
+ }
+
+ return 0;
+}
+
+static int spi100_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ /* First byte is cmd which can not be sent through the buffer. */
+ unsigned char cmd = *writearr++;
+ writecnt--;
+ msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
+ mmio_writeb(cmd, sb600_spibar + 0);
+
+ int ret = check_readwritecnt(flash, writecnt, readcnt);
+ if (ret != 0)
+ return ret;
+
+ /* Use the extended TxByteCount and RxByteCount registers. */
+ mmio_writeb(writecnt, sb600_spibar + 0x48);
+ mmio_writeb(readcnt, sb600_spibar + 0x4b);
+
+ msg_pspew("Filling buffer: ");
+ int count;
+ for (count = 0; count < writecnt; count++) {
+ msg_pspew("[%02x]", writearr[count]);
+ mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
+ }
+ msg_pspew("\n");
+
+ execute_command();
+
+ msg_pspew("Reading buffer: ");
+ for (count = 0; count < readcnt; count++) {
+ readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
+ msg_pspew("[%02x]", readarr[count]);
+ }
+ msg_pspew("\n");
+
+ return 0;
+}
+
+struct spispeed {
+ const char *const name;
+ const uint8_t speed;
+};
+
+static const struct spispeed spispeeds[] = {
+ { "66 MHz", 0x00 },
+ { "33 MHz", 0x01 },
+ { "22 MHz", 0x02 },
+ { "16.5 MHz", 0x03 },
+ { "100 MHz", 0x04 },
+ { "Reserved", 0x05 },
+ { "Reserved", 0x06 },
+ { "800 kHz", 0x07 },
+};
+
+static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
+{
+ bool success = false;
+ uint8_t speed = spispeed->speed;
+
+ msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
+ if (amd_gen >= CHIPSET_YANGTZE) {
+ rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
+ uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
+ success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
+ ((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
+ } else {
+ rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
+ success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
+ }
+
+ if (!success) {
+ msg_perr("Setting SPI clock failed.\n");
+ return 1;
+ }
+ return 0;
+}
+
+static int set_mode(struct pci_dev *dev, uint8_t read_mode)
+{
+ uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
+ tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
+ tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
+ rmmio_writel(tmp, sb600_spibar + 0x00);
+ if (tmp != mmio_readl(sb600_spibar + 0x00))
+ return 1;
+ return 0;
+}
+
+static int handle_speed(struct pci_dev *dev)
+{
+ uint32_t tmp;
+ uint8_t spispeed_idx = 3; /* Default to 16.5 MHz */
+
+ char *spispeed = extract_programmer_param("spispeed");
+ if (spispeed != NULL) {
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(spispeeds); i++) {
+ if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
+ spispeed_idx = i;
+ break;
+ }
+ }
+ /* "reserved" is not a valid speed.
+ * Error out on speeds not present in the spispeeds array.
+ * Only Yangtze supports the second half of indices.
+ * No 66 MHz before SB8xx. */
+ if ((strcasecmp(spispeed, "reserved") == 0) ||
+ (i == ARRAY_SIZE(spispeeds)) ||
+ (amd_gen < CHIPSET_YANGTZE && spispeed_idx > 3) ||
+ (amd_gen < CHIPSET_SB89XX && spispeed_idx == 0)) {
+ msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
+ free(spispeed);
+ return 1;
+ }
+ free(spispeed);
+ }
+
+ /* See the chipset support matrix for SPI Base_Addr below for an explanation of the symbols used.
+ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson234 bolton/yangtze
+ * 18 rsvd <- fastReadEnable ? <- ? SpiReadMode[0]
+ * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]
+ */
+ if (amd_gen >= CHIPSET_BOLTON) {
+ static const char *spireadmodes[] = {
+ "Normal (up to 33 MHz)", /* 0 */
+ "Reserved", /* 1 */
+ "Dual IO (1-1-2)", /* 2 */
+ "Quad IO (1-1-4)", /* 3 */
+ "Dual IO (1-2-2)", /* 4 */
+ "Quad IO (1-4-4)", /* 5 */
+ "Normal (up to 66 MHz)", /* 6 */
+ "Fast Read", /* 7 (Not defined in the Bolton datasheet.) */
+ };
+ tmp = mmio_readl(sb600_spibar + 0x00);
+ uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
+ msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
+ if (read_mode != 6) {
+ read_mode = 6; /* Default to "Normal (up to 66 MHz)" */
+ if (set_mode(dev, read_mode) != 0) {
+ msg_perr("Setting read mode to \"%s\" failed.\n", spireadmodes[read_mode]);
+ return 1;
+ }
+ msg_pdbg("Setting read mode to \"%s\" succeeded.\n", spireadmodes[read_mode]);
+ }
+
+ if (amd_gen >= CHIPSET_YANGTZE) {
+ tmp = mmio_readb(sb600_spibar + 0x20);
+ msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
+ if ((tmp & 0x1) == 0) {
+ rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
+ tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
+ if (tmp == 0) {
+ msg_perr("Enabling Spi100 failed.\n");
+ return 1;
+ }
+ msg_pdbg("Enabling Spi100 succeeded.\n");
+ }
+
+ tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
+ msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
+ msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
+ msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
+ msg_pdbg("TpmSpeedNew is %s\n", spispeeds[(tmp >> 0) & 0xf].name);
+ }
+ } else {
+ if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
+ bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
+ msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
+ if (fast_read) {
+ msg_pdbg("Disabling them temporarily.\n");
+ rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18),
+ sb600_spibar + 0x00);
+ }
+ }
+ tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
+ msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name);
+ }
+ return set_speed(dev, &spispeeds[spispeed_idx]);
+}
+
+static int handle_imc(struct pci_dev *dev)
+{
+ /* Handle IMC everywhere but sb600 which does not have one. */
+ if (amd_gen == CHIPSET_SB6XX)
+ return 0;
+
+ bool amd_imc_force = false;
+ char *arg = extract_programmer_param("amd_imc_force");
+ if (arg && !strcmp(arg, "yes")) {
+ amd_imc_force = true;
+ msg_pspew("amd_imc_force enabled.\n");
+ } else if (arg && !strlen(arg)) {
+ msg_perr("Missing argument for amd_imc_force.\n");
+ free(arg);
+ return 1;
+ } else if (arg) {
+ msg_perr("Unknown argument for amd_imc_force: \"%s\" (not \"yes\").\n", arg);
+ free(arg);
+ return 1;
+ }
+ free(arg);
+
+ /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at
+ * IMCEnable(Strap) and Override EcEnable(Strap) (sb8xx, sb9xx?, a50, Bolton: Misc_Reg: 80h-87h;
+ * sb7xx, sp5100: PM_Reg: B0h-B1h) etc. */
+ uint8_t reg = pci_read_byte(dev, 0x40);
+ if ((reg & (1 << 7)) == 0) {
+ msg_pdbg("IMC is not active.\n");
+ return 0;
+ }
+
+ if (!amd_imc_force)
+ programmer_may_write = 0;
+ msg_pinfo("Writes have been disabled for safety reasons because the presence of the IMC\n"
+ "was detected and it could interfere with accessing flash memory. Flashrom will\n"
+ "try to disable it temporarily but even then this might not be safe:\n"
+ "when it is re-enabled and after a reboot it expects to find working code\n"
+ "in the flash and it is unpredictable what happens if there is none.\n"
+ "\n"
+ "To be safe make sure that there is a working IMC firmware at the right\n"
+ "location in the image you intend to write and do not attempt to erase.\n"
+ "\n"
+ "You can enforce write support with the amd_imc_force programmer option.\n");
+ if (amd_imc_force)
+ msg_pinfo("Continuing with write support because the user forced us to!\n");
+
+ return amd_imc_shutdown(dev);
+}
+
+int sb600_probe_spi(struct pci_dev *dev)
+{
+ struct pci_dev *smbus_dev;
+ uint32_t tmp;
+ uint8_t reg;
+
+ /* Read SPI_BaseAddr */
+ tmp = pci_read_long(dev, 0xa0);
+ tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */
+ msg_pdbg("SPI base address is at 0x%x\n", tmp);
+
+ /* If the BAR has address 0, it is unlikely SPI is used. */
+ if (!tmp)
+ return 0;
+
+ /* Physical memory has to be mapped at page (4k) boundaries. */
+ sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
+ if (sb600_spibar == ERROR_PTR)
+ return ERROR_FATAL;
+
+ /* The low bits of the SPI base address are used as offset into
+ * the mapped page.
+ */
+ sb600_spibar += tmp & 0xfff;
+
+ determine_generation(dev);
+ if (amd_gen == CHIPSET_AMD_UNKNOWN) {
+ msg_perr("Could not determine chipset generation.");
+ return ERROR_NONFATAL;
+ }
+
+ /* How to read the following table and similar ones in this file:
+ * "?" means we have no datasheet for this chipset generation or it doesn't have any relevant info.
+ * "<-" means the bit/register meaning is identical to the next non-"?" chipset to the left. "<-" thus
+ * never refers to another "?".
+ * If a "?" chipset is between two chipsets with identical meaning, we assume the meaning didn't change
+ * twice in between, i.e. the meaning is unchanged for the "?" chipset. Usually we assume that
+ * succeeding hardware supports the same functionality as its predecessor unless proven different by
+ * tests or documentation, hence "?" will often be implemented equally to "<-".
+ *
+ * Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0)
+ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
+ * 3 rsvd <- <- ? <- ? RouteTpm2Spi
+ * 2 rsvd AbortEnable rsvd ? <- ? <-
+ * 1 rsvd SpiRomEnable <- ? <- ? <-
+ * 0 rsvd AltSpiCSEnable rsvd ? <- ? <-
+ */
+ if (amd_gen >= CHIPSET_SB7XX) {
+ tmp = pci_read_long(dev, 0xa0);
+ msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1);
+ if (amd_gen == CHIPSET_SB7XX)
+ msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1);
+ else if (amd_gen == CHIPSET_YANGTZE)
+ msg_pdbg(", RouteTpm2Sp=%i", (tmp >> 3) & 0x1);
+
+ tmp = pci_read_byte(dev, 0xba);
+ msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2);
+
+ tmp = pci_read_byte(dev, 0xbb);
+ /* FIXME: Set bit 3,6,7 if not already set.
+ * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
+ * See doc 42413 AMD SB700/710/750 RPR.
+ */
+ if (amd_gen == CHIPSET_SB7XX)
+ msg_pdbg(", SpiOpEnInLpcMode=%i", (tmp >> 5) & 0x1);
+ msg_pdbg(", PrefetchEnSPIFromHost=%i\n", tmp & 0x1);
+ }
+
+ /* Chipset support matrix for SPI_Cntrl0 (spibar + 0x0)
+ * See the chipset support matrix for SPI Base_Addr above for an explanation of the symbols used.
+ * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
+ * 17 rsvd <- <- ? <- ? <-
+ * 18 rsvd <- fastReadEnable<1> ? <- ? SpiReadMode[0]<1>
+ * 19 SpiArbEnable <- <- ? <- ? <-
+ * 20 (FifoPtrClr) <- <- ? <- ? <-
+ * 21 (FifoPtrInc) <- <- ? <- ? IllegalAccess
+ * 22 SpiAccessMacRomEn <- <- ? <- ? <-
+ * 23 SpiHostAccessRomEn <- <- ? <- ? <-
+ * 24:26 ArbWaitCount <- <- ? <- ? <-
+ * 27 SpiBridgeDisable <- <- ? <- ? rsvd
+ * 28 rsvd DropOneClkOnRd = SPIClkGate ? <- ? <-
+ * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]<1>
+ * 31 rsvd <- SpiBusy ? <- ? <-
+ *
+ * <1> see handle_speed
+ */
+ tmp = mmio_readl(sb600_spibar + 0x00);
+ msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
+ if (amd_gen == CHIPSET_YANGTZE)
+ msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
+
+ msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i",
+ (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7);
+
+ if (amd_gen != CHIPSET_YANGTZE)
+ msg_pdbg(", SpiBridgeDisable=%i", (tmp >> 27) & 0x1);
+
+ switch (amd_gen) {
+ case CHIPSET_SB7XX:
+ msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1);
+ case CHIPSET_SB89XX:
+ case CHIPSET_HUDSON234:
+ case CHIPSET_YANGTZE:
+ msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1);
+ default: break;
+ }
+ msg_pdbg("\n");
+
+ if (((tmp >> 22) & 0x1) == 0 || ((tmp >> 23) & 0x1) == 0) {
+ msg_perr("ERROR: State of SpiAccessMacRomEn or SpiHostAccessRomEn prohibits full access.\n");
+ return ERROR_NONFATAL;
+ }
+
+ if (amd_gen >= CHIPSET_SB89XX) {
+ tmp = mmio_readb(sb600_spibar + 0x1D);
+ msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
+ /* FIXME: Handle SpiProtect* configuration on Yangtze. */
+ }
+
+ /* Look for the SMBus device. */
+ smbus_dev = pci_dev_find(0x1002, 0x4385);
+ if (!smbus_dev) {
+ smbus_dev = pci_dev_find(0x1022, 0x780b); /* AMD FCH */
+ if (!smbus_dev) {
+ msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
+ return ERROR_NONFATAL;
+ }
+ }
+
+ /* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
+ /* GPIO11/SPI_DO and GPIO12/SPI_DI status */
+ reg = pci_read_byte(smbus_dev, 0xAB);
+ reg &= 0xC0;
+ msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
+ msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
+ if (reg != 0x00) {
+ msg_pdbg("Not enabling SPI");
+ return 0;
+ }
+ /* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
+ reg = pci_read_byte(smbus_dev, 0x83);
+ reg &= 0xC0;
+ msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
+ msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
+ /* SPI_HOLD is not used on all boards, filter it out. */
+ if ((reg & 0x80) != 0x00) {
+ msg_pdbg("Not enabling SPI");
+ return 0;
+ }
+ /* GPIO47/SPI_CLK status */
+ reg = pci_read_byte(smbus_dev, 0xA7);
+ reg &= 0x40;
+ msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
+ if (reg != 0x00) {
+ msg_pdbg("Not enabling SPI");
+ return 0;
+ }
+
+ if (handle_speed(dev) != 0)
+ return ERROR_FATAL;
+
+ if (handle_imc(dev) != 0)
+ return ERROR_FATAL;
+
+ /* Starting with Yangtze the SPI controller got a different interface with a much bigger buffer. */
+ if (amd_gen != CHIPSET_YANGTZE)
+ register_spi_master(&spi_master_sb600);
+ else
+ register_spi_master(&spi_master_yangtze);
+ return 0;
+}
+
+#endif
diff --git a/serial.c b/serial.c
new file mode 100644
index 0000000..a64a51d
--- /dev/null
+++ b/serial.c
@@ -0,0 +1,597 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009 Urja Rannikko <urjaman@gmail.com>
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "platform.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <inttypes.h>
+#if IS_WINDOWS
+#include <conio.h>
+#else
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#endif
+#include "flash.h"
+#include "programmer.h"
+
+fdtype sp_fd = SER_INV_FD;
+
+/* There is no way defined by POSIX to use arbitrary baud rates. It only defines some macros that can be used to
+ * specify respective baud rates and many implementations extend this list with further macros, cf. TERMIOS(3)
+ * and http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/uapi/asm-generic/termbits.h
+ * The code below creates a mapping in sp_baudtable between these macros and the numerical baud rates to deal
+ * with numerical user input.
+ *
+ * On Linux there is a non-standard way to use arbitrary baud rates that flashrom does not support (yet), cf.
+ * http://www.downtowndougbrown.com/2013/11/linux-custom-serial-baud-rates/
+ *
+ * On Windows there exist similar macros (starting with CBR_ instead of B) but they are only defined for
+ * backwards compatibility and the API supports arbitrary baud rates in the same manner as the macros, see
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx
+ */
+#if !IS_WINDOWS
+struct baudentry {
+ int flag;
+ unsigned int baud;
+};
+#define BAUDENTRY(baud) { B##baud, baud },
+
+static const struct baudentry sp_baudtable[] = {
+ BAUDENTRY(9600) /* unconditional default */
+#ifdef B19200
+ BAUDENTRY(19200)
+#endif
+#ifdef B38400
+ BAUDENTRY(38400)
+#endif
+#ifdef B57600
+ BAUDENTRY(57600)
+#endif
+#ifdef B115200
+ BAUDENTRY(115200)
+#endif
+#ifdef B230400
+ BAUDENTRY(230400)
+#endif
+#ifdef B460800
+ BAUDENTRY(460800)
+#endif
+#ifdef B500000
+ BAUDENTRY(500000)
+#endif
+#ifdef B576000
+ BAUDENTRY(576000)
+#endif
+#ifdef B921600
+ BAUDENTRY(921600)
+#endif
+#ifdef B1000000
+ BAUDENTRY(1000000)
+#endif
+#ifdef B1152000
+ BAUDENTRY(1152000)
+#endif
+#ifdef B1500000
+ BAUDENTRY(1500000)
+#endif
+#ifdef B2000000
+ BAUDENTRY(2000000)
+#endif
+#ifdef B2500000
+ BAUDENTRY(2500000)
+#endif
+#ifdef B3000000
+ BAUDENTRY(3000000)
+#endif
+#ifdef B3500000
+ BAUDENTRY(3500000)
+#endif
+#ifdef B4000000
+ BAUDENTRY(4000000)
+#endif
+ {0, 0} /* Terminator */
+};
+
+static const struct baudentry *round_baud(unsigned int baud)
+{
+ int i;
+ /* Round baud rate to next lower entry in sp_baudtable if it exists, else use the lowest entry. */
+ for (i = ARRAY_SIZE(sp_baudtable) - 2; i >= 0 ; i--) {
+ if (sp_baudtable[i].baud == baud)
+ return &sp_baudtable[i];
+
+ if (sp_baudtable[i].baud < baud) {
+ msg_pwarn("Warning: given baudrate %d rounded down to %d.\n",
+ baud, sp_baudtable[i].baud);
+ return &sp_baudtable[i];
+ }
+ }
+ msg_pinfo("Using slowest possible baudrate: %d.\n", sp_baudtable[0].baud);
+ return &sp_baudtable[0];
+}
+#endif
+
+/* Uses msg_perr to print the last system error.
+ * Prints "Error: " followed first by \c msg and then by the description of the last error retrieved via
+ * strerror() or FormatMessage() and ending with a linebreak. */
+static void msg_perr_strerror(const char *msg)
+{
+ msg_perr("Error: %s", msg);
+#if IS_WINDOWS
+ char *lpMsgBuf;
+ DWORD nErr = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, nErr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
+ msg_perr(lpMsgBuf);
+ /* At least some formatted messages contain a line break at the end. Make sure to always print one */
+ if (lpMsgBuf[strlen(lpMsgBuf)-1] != '\n')
+ msg_perr("\n");
+ LocalFree(lpMsgBuf);
+#else
+ msg_perr("%s\n", strerror(errno));
+#endif
+}
+
+int serialport_config(fdtype fd, int baud)
+{
+ if (fd == SER_INV_FD) {
+ msg_perr("%s: File descriptor is invalid.\n", __func__);
+ return 1;
+ }
+
+#if IS_WINDOWS
+ DCB dcb;
+ if (!GetCommState(fd, &dcb)) {
+ msg_perr_strerror("Could not fetch original serial port configuration: ");
+ return 1;
+ }
+ if (baud >= 0) {
+ dcb.BaudRate = baud;
+ }
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.StopBits = ONESTOPBIT;
+ if (!SetCommState(fd, &dcb)) {
+ msg_perr_strerror("Could not change serial port configuration: ");
+ return 1;
+ }
+ if (!GetCommState(fd, &dcb)) {
+ msg_perr_strerror("Could not fetch new serial port configuration: ");
+ return 1;
+ }
+ msg_pdbg("Baud rate is %ld.\n", dcb.BaudRate);
+#else
+ struct termios wanted, observed;
+ if (tcgetattr(fd, &observed) != 0) {
+ msg_perr_strerror("Could not fetch original serial port configuration: ");
+ return 1;
+ }
+ wanted = observed;
+ if (baud >= 0) {
+ const struct baudentry *entry = round_baud(baud);
+ if (cfsetispeed(&wanted, entry->flag) != 0 || cfsetospeed(&wanted, entry->flag) != 0) {
+ msg_perr_strerror("Could not set serial baud rate: ");
+ return 1;
+ }
+ }
+ wanted.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ wanted.c_cflag |= (CS8 | CLOCAL | CREAD);
+ wanted.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ wanted.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL | IGNCR | INLCR);
+ wanted.c_oflag &= ~OPOST;
+ if (tcsetattr(fd, TCSANOW, &wanted) != 0) {
+ msg_perr_strerror("Could not change serial port configuration: ");
+ return 1;
+ }
+ if (tcgetattr(fd, &observed) != 0) {
+ msg_perr_strerror("Could not fetch new serial port configuration: ");
+ return 1;
+ }
+ if (observed.c_cflag != wanted.c_cflag ||
+ observed.c_lflag != wanted.c_lflag ||
+ observed.c_iflag != wanted.c_iflag ||
+ observed.c_oflag != wanted.c_oflag) {
+ msg_pwarn("Some requested serial options did not stick, continuing anyway.\n");
+ msg_pdbg(" observed wanted\n"
+ "c_cflag: 0x%08lX 0x%08lX\n"
+ "c_lflag: 0x%08lX 0x%08lX\n"
+ "c_iflag: 0x%08lX 0x%08lX\n"
+ "c_oflag: 0x%08lX 0x%08lX\n",
+ (long)observed.c_cflag, (long)wanted.c_cflag,
+ (long)observed.c_lflag, (long)wanted.c_lflag,
+ (long)observed.c_iflag, (long)wanted.c_iflag,
+ (long)observed.c_oflag, (long)wanted.c_oflag
+ );
+ }
+ if (cfgetispeed(&observed) != cfgetispeed(&wanted) ||
+ cfgetospeed(&observed) != cfgetospeed(&wanted)) {
+ msg_pwarn("Could not set baud rates exactly.\n");
+ msg_pdbg("Actual baud flags are: ispeed: 0x%08lX, ospeed: 0x%08lX\n",
+ (long)cfgetispeed(&observed), (long)cfgetospeed(&observed));
+ }
+ // FIXME: display actual baud rate - at least if none was specified by the user.
+#endif
+ return 0;
+}
+
+fdtype sp_openserport(char *dev, int baud)
+{
+ fdtype fd;
+#if IS_WINDOWS
+ char *dev2 = dev;
+ if ((strlen(dev) > 3) &&
+ (tolower((unsigned char)dev[0]) == 'c') &&
+ (tolower((unsigned char)dev[1]) == 'o') &&
+ (tolower((unsigned char)dev[2]) == 'm')) {
+ dev2 = malloc(strlen(dev) + 5);
+ if (!dev2) {
+ msg_perr_strerror("Out of memory: ");
+ return SER_INV_FD;
+ }
+ strcpy(dev2, "\\\\.\\");
+ strcpy(dev2 + 4, dev);
+ }
+ fd = CreateFile(dev2, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ if (dev2 != dev)
+ free(dev2);
+ if (fd == INVALID_HANDLE_VALUE) {
+ msg_perr_strerror("Cannot open serial port: ");
+ return SER_INV_FD;
+ }
+ if (serialport_config(fd, baud) != 0) {
+ CloseHandle(fd);
+ return SER_INV_FD;
+ }
+ return fd;
+#else
+ fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); // Use O_NDELAY to ignore DCD state
+ if (fd < 0) {
+ msg_perr_strerror("Cannot open serial port: ");
+ return SER_INV_FD;
+ }
+
+ /* Ensure that we use blocking I/O */
+ const int flags = fcntl(fd, F_GETFL);
+ if (flags == -1) {
+ msg_perr_strerror("Could not get serial port mode: ");
+ goto err;
+ }
+ if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
+ msg_perr_strerror("Could not set serial port mode to blocking: ");
+ goto err;
+ }
+
+ if (serialport_config(fd, baud) != 0) {
+ goto err;
+ }
+ return fd;
+err:
+ close(fd);
+ return SER_INV_FD;
+#endif
+}
+
+void sp_set_pin(enum SP_PIN pin, int val) {
+#if IS_WINDOWS
+ DWORD ctl;
+
+ if(pin == PIN_TXD) {
+ ctl = val ? SETBREAK: CLRBREAK;
+ }
+ else if(pin == PIN_DTR) {
+ ctl = val ? SETDTR: CLRDTR;
+ }
+ else {
+ ctl = val ? SETRTS: CLRRTS;
+ }
+ EscapeCommFunction(sp_fd, ctl);
+#else
+ int ctl, s;
+
+ if(pin == PIN_TXD) {
+ ioctl(sp_fd, val ? TIOCSBRK : TIOCCBRK, 0);
+ }
+ else {
+ s = (pin == PIN_DTR) ? TIOCM_DTR : TIOCM_RTS;
+ ioctl(sp_fd, TIOCMGET, &ctl);
+
+ if (val) {
+ ctl |= s;
+ }
+ else {
+ ctl &= ~s;
+ }
+ ioctl(sp_fd, TIOCMSET, &ctl);
+ }
+#endif
+}
+
+int sp_get_pin(enum SP_PIN pin) {
+ int s;
+#if IS_WINDOWS
+ DWORD ctl;
+
+ s = (pin == PIN_CTS) ? MS_CTS_ON : MS_DSR_ON;
+ GetCommModemStatus(sp_fd, &ctl);
+#else
+ int ctl;
+ s = (pin == PIN_CTS) ? TIOCM_CTS : TIOCM_DSR;
+ ioctl(sp_fd, TIOCMGET, &ctl);
+#endif
+
+ return ((ctl & s) ? 1 : 0);
+
+}
+
+void sp_flush_incoming(void)
+{
+#if IS_WINDOWS
+ PurgeComm(sp_fd, PURGE_RXCLEAR);
+#else
+ /* FIXME: error handling */
+ tcflush(sp_fd, TCIFLUSH);
+#endif
+ return;
+}
+
+int serialport_shutdown(void *data)
+{
+#if IS_WINDOWS
+ CloseHandle(sp_fd);
+#else
+ close(sp_fd);
+#endif
+ return 0;
+}
+
+int serialport_write(const unsigned char *buf, unsigned int writecnt)
+{
+#if IS_WINDOWS
+ DWORD tmp = 0;
+#else
+ ssize_t tmp = 0;
+#endif
+ unsigned int empty_writes = 250; /* results in a ca. 125ms timeout */
+
+ while (writecnt > 0) {
+#if IS_WINDOWS
+ WriteFile(sp_fd, buf, writecnt, &tmp, NULL);
+#else
+ tmp = write(sp_fd, buf, writecnt);
+#endif
+ if (tmp == -1) {
+ msg_perr("Serial port write error!\n");
+ return 1;
+ }
+ if (!tmp) {
+ msg_pdbg2("Empty write\n");
+ empty_writes--;
+ internal_delay(500);
+ if (empty_writes == 0) {
+ msg_perr("Serial port is unresponsive!\n");
+ return 1;
+ }
+ }
+ writecnt -= tmp;
+ buf += tmp;
+ }
+
+ return 0;
+}
+
+int serialport_read(unsigned char *buf, unsigned int readcnt)
+{
+#if IS_WINDOWS
+ DWORD tmp = 0;
+#else
+ ssize_t tmp = 0;
+#endif
+
+ while (readcnt > 0) {
+#if IS_WINDOWS
+ ReadFile(sp_fd, buf, readcnt, &tmp, NULL);
+#else
+ tmp = read(sp_fd, buf, readcnt);
+#endif
+ if (tmp == -1) {
+ msg_perr("Serial port read error!\n");
+ return 1;
+ }
+ if (!tmp)
+ msg_pdbg2("Empty read\n");
+ readcnt -= tmp;
+ buf += tmp;
+ }
+
+ return 0;
+}
+
+/* Tries up to timeout ms to read readcnt characters and places them into the array starting at c. Returns
+ * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors.
+ * If really_read is not NULL, this function sets its contents to the number of bytes read successfully. */
+int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read)
+{
+ int ret = 1;
+ /* disable blocked i/o and declare platform-specific variables */
+#if IS_WINDOWS
+ DWORD rv;
+ COMMTIMEOUTS oldTimeout;
+ COMMTIMEOUTS newTimeout = {
+ .ReadIntervalTimeout = MAXDWORD,
+ .ReadTotalTimeoutMultiplier = 0,
+ .ReadTotalTimeoutConstant = 0,
+ .WriteTotalTimeoutMultiplier = 0,
+ .WriteTotalTimeoutConstant = 0
+ };
+ if(!GetCommTimeouts(sp_fd, &oldTimeout)) {
+ msg_perr_strerror("Could not get serial port timeout settings: ");
+ return -1;
+ }
+ if(!SetCommTimeouts(sp_fd, &newTimeout)) {
+ msg_perr_strerror("Could not set serial port timeout settings: ");
+ return -1;
+ }
+#else
+ ssize_t rv;
+ const int flags = fcntl(sp_fd, F_GETFL);
+ if (flags == -1) {
+ msg_perr_strerror("Could not get serial port mode: ");
+ return -1;
+ }
+ if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+ msg_perr_strerror("Could not set serial port mode to non-blocking: ");
+ return -1;
+ }
+#endif
+
+ int i;
+ int rd_bytes = 0;
+ for (i = 0; i < timeout; i++) {
+ msg_pspew("readcnt %d rd_bytes %d\n", readcnt, rd_bytes);
+#if IS_WINDOWS
+ ReadFile(sp_fd, c + rd_bytes, readcnt - rd_bytes, &rv, NULL);
+ msg_pspew("read %lu bytes\n", rv);
+#else
+ rv = read(sp_fd, c + rd_bytes, readcnt - rd_bytes);
+ msg_pspew("read %zd bytes\n", rv);
+#endif
+ if ((rv == -1) && (errno != EAGAIN)) {
+ msg_perr_strerror("Serial port read error: ");
+ ret = -1;
+ break;
+ }
+ if (rv > 0)
+ rd_bytes += rv;
+ if (rd_bytes == readcnt) {
+ ret = 0;
+ break;
+ }
+ internal_delay(1000); /* 1ms units */
+ }
+ if (really_read != NULL)
+ *really_read = rd_bytes;
+
+ /* restore original blocking behavior */
+#if IS_WINDOWS
+ if (!SetCommTimeouts(sp_fd, &oldTimeout)) {
+ msg_perr_strerror("Could not restore serial port timeout settings: ");
+ ret = -1;
+ }
+#else
+ if (fcntl(sp_fd, F_SETFL, flags) != 0) {
+ msg_perr_strerror("Could not restore serial port mode to blocking: ");
+ ret = -1;
+ }
+#endif
+ return ret;
+}
+
+/* Tries up to timeout ms to write writecnt characters from the array starting at buf. Returns
+ * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors.
+ * If really_wrote is not NULL, this function sets its contents to the number of bytes written successfully. */
+int serialport_write_nonblock(const unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote)
+{
+ int ret = 1;
+ /* disable blocked i/o and declare platform-specific variables */
+#if IS_WINDOWS
+ DWORD rv;
+ COMMTIMEOUTS oldTimeout;
+ COMMTIMEOUTS newTimeout = {
+ .ReadIntervalTimeout = MAXDWORD,
+ .ReadTotalTimeoutMultiplier = 0,
+ .ReadTotalTimeoutConstant = 0,
+ .WriteTotalTimeoutMultiplier = 0,
+ .WriteTotalTimeoutConstant = 0
+ };
+ if(!GetCommTimeouts(sp_fd, &oldTimeout)) {
+ msg_perr_strerror("Could not get serial port timeout settings: ");
+ return -1;
+ }
+ if(!SetCommTimeouts(sp_fd, &newTimeout)) {
+ msg_perr_strerror("Could not set serial port timeout settings: ");
+ return -1;
+ }
+#else
+ ssize_t rv;
+ const int flags = fcntl(sp_fd, F_GETFL);
+ if (flags == -1) {
+ msg_perr_strerror("Could not get serial port mode: ");
+ return -1;
+ }
+ if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+ msg_perr_strerror("Could not set serial port mode to non-blocking: ");
+ return -1;
+ }
+#endif
+
+ int i;
+ int wr_bytes = 0;
+ for (i = 0; i < timeout; i++) {
+ msg_pspew("writecnt %d wr_bytes %d\n", writecnt, wr_bytes);
+#if IS_WINDOWS
+ WriteFile(sp_fd, buf + wr_bytes, writecnt - wr_bytes, &rv, NULL);
+ msg_pspew("wrote %lu bytes\n", rv);
+#else
+ rv = write(sp_fd, buf + wr_bytes, writecnt - wr_bytes);
+ msg_pspew("wrote %zd bytes\n", rv);
+#endif
+ if ((rv == -1) && (errno != EAGAIN)) {
+ msg_perr_strerror("Serial port write error: ");
+ ret = -1;
+ break;
+ }
+ if (rv > 0) {
+ wr_bytes += rv;
+ if (wr_bytes == writecnt) {
+ msg_pspew("write successful\n");
+ ret = 0;
+ break;
+ }
+ }
+ internal_delay(1000); /* 1ms units */
+ }
+ if (really_wrote != NULL)
+ *really_wrote = wr_bytes;
+
+ /* restore original blocking behavior */
+#if IS_WINDOWS
+ if (!SetCommTimeouts(sp_fd, &oldTimeout)) {
+ msg_perr_strerror("Could not restore serial port timeout settings: ");
+ return -1;
+ }
+#else
+ if (fcntl(sp_fd, F_SETFL, flags) != 0) {
+ msg_perr_strerror("Could not restore serial port blocking behavior: ");
+ return -1;
+ }
+#endif
+ return ret;
+}
diff --git a/serprog.c b/serprog.c
new file mode 100644
index 0000000..98aac83
--- /dev/null
+++ b/serprog.c
@@ -0,0 +1,969 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2009, 2011 Urja Rannikko <urjaman@gmail.com>
+ * Copyright (C) 2009 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "platform.h"
+
+#include <stdio.h>
+#if ! IS_WINDOWS /* stuff (presumably) needed for sockets only */
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+#if IS_WINDOWS
+#include <conio.h>
+#else
+#include <termios.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include "flash.h"
+#include "programmer.h"
+#include "chipdrivers.h"
+#include "serprog.h"
+
+#define MSGHEADER "serprog: "
+
+/*
+ * FIXME: This prototype was added to help reduce diffs for the shutdown
+ * registration patch, which shifted many lines of code to place
+ * serprog_shutdown() before serprog_init(). It should be removed soon.
+ */
+static int serprog_shutdown(void *data);
+
+static uint16_t sp_device_serbuf_size = 16;
+static uint16_t sp_device_opbuf_size = 300;
+/* Bitmap of supported commands */
+static uint8_t sp_cmdmap[32];
+
+/* sp_prev_was_write used to detect writes with contiguous addresses
+ and combine them to write-n's */
+static int sp_prev_was_write = 0;
+/* sp_write_n_addr used as the starting addr of the currently
+ combined write-n operation */
+static uint32_t sp_write_n_addr;
+/* The maximum length of an write_n operation; 0 = write-n not supported */
+static uint32_t sp_max_write_n = 0;
+/* The maximum length of a read_n operation; 0 = 2^24 */
+static uint32_t sp_max_read_n = 0;
+
+/* A malloc'd buffer for combining the operation's data
+ and a counter that tells how much data is there. */
+static uint8_t *sp_write_n_buf;
+static uint32_t sp_write_n_bytes = 0;
+
+/* sp_streamed_* used for flow control checking */
+static int sp_streamed_transmit_ops = 0;
+static int sp_streamed_transmit_bytes = 0;
+
+/* sp_opbuf_usage used for counting the amount of
+ on-device operation buffer used */
+static int sp_opbuf_usage = 0;
+/* if true causes sp_docommand to automatically check
+ whether the command is supported before doing it */
+static int sp_check_avail_automatic = 0;
+
+#if ! IS_WINDOWS
+static int sp_opensocket(char *ip, unsigned int port)
+{
+ int flag = 1;
+ struct hostent *hostPtr = NULL;
+ union { struct sockaddr_in si; struct sockaddr s; } sp = {};
+ int sock;
+ msg_pdbg(MSGHEADER "IP %s port %d\n", ip, port);
+ sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) {
+ msg_perr("Error: serprog cannot open socket: %s\n", strerror(errno));
+ return -1;
+ }
+ hostPtr = gethostbyname(ip);
+ if (NULL == hostPtr) {
+ hostPtr = gethostbyaddr(ip, strlen(ip), AF_INET);
+ if (NULL == hostPtr) {
+ close(sock);
+ msg_perr("Error: cannot resolve %s\n", ip);
+ return -1;
+ }
+ }
+ sp.si.sin_family = AF_INET;
+ sp.si.sin_port = htons(port);
+ (void)memcpy(&sp.si.sin_addr, hostPtr->h_addr_list[0], hostPtr->h_length);
+ if (connect(sock, &sp.s, sizeof(sp.si)) < 0) {
+ close(sock);
+ msg_perr("Error: serprog cannot connect: %s\n", strerror(errno));
+ return -1;
+ }
+ /* We are latency limited, and sometimes do write-write-read *
+ * (write-n) - so enable TCP_NODELAY. */
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int))) {
+ close(sock);
+ msg_perr("Error: serprog cannot set socket options: %s\n", strerror(errno));
+ return -1;
+ }
+ return sock;
+}
+#endif
+
+/* Synchronize: a bit tricky algorithm that tries to (and in my tests has *
+ * always succeeded in) bring the serial protocol to known waiting-for- *
+ * command state - uses nonblocking I/O - rest of the driver uses *
+ * blocking read - TODO: add an alarm() timer for the rest of the app on *
+ * serial operations, though not such a big issue as the first thing to *
+ * do is synchronize (eg. check that device is alive). */
+static int sp_synchronize(void)
+{
+ int i;
+ unsigned char buf[8];
+ /* First sends 8 NOPs, then flushes the return data - should cause *
+ * the device serial parser to get to a sane state, unless if it *
+ * is waiting for a real long write-n. */
+ memset(buf, S_CMD_NOP, 8);
+ if (serialport_write_nonblock(buf, 8, 1, NULL) != 0) {
+ goto err_out;
+ }
+ /* A second should be enough to get all the answers to the buffer */
+ internal_delay(1000 * 1000);
+ sp_flush_incoming();
+
+ /* Then try up to 8 times to send syncnop and get the correct special *
+ * return of NAK+ACK. Timing note: up to 10 characters, 10*50ms = *
+ * up to 500ms per try, 8*0.5s = 4s; +1s (above) = up to 5s sync *
+ * attempt, ~1s if immediate success. */
+ for (i = 0; i < 8; i++) {
+ int n;
+ unsigned char c = S_CMD_SYNCNOP;
+ if (serialport_write_nonblock(&c, 1, 1, NULL) != 0) {
+ goto err_out;
+ }
+ msg_pdbg(".");
+ fflush(stdout);
+ for (n = 0; n < 10; n++) {
+ int ret = serialport_read_nonblock(&c, 1, 50, NULL);
+ if (ret < 0)
+ goto err_out;
+ if (ret > 0 || c != S_NAK)
+ continue;
+ ret = serialport_read_nonblock(&c, 1, 20, NULL);
+ if (ret < 0)
+ goto err_out;
+ if (ret > 0 || c != S_ACK)
+ continue;
+ c = S_CMD_SYNCNOP;
+ if (serialport_write_nonblock(&c, 1, 1, NULL) != 0) {
+ goto err_out;
+ }
+ ret = serialport_read_nonblock(&c, 1, 500, NULL);
+ if (ret < 0)
+ goto err_out;
+ if (ret > 0 || c != S_NAK)
+ break; /* fail */
+ ret = serialport_read_nonblock(&c, 1, 100, NULL);
+ if (ret > 0 || ret < 0)
+ goto err_out;
+ if (c != S_ACK)
+ break; /* fail */
+ msg_pdbg("\n");
+ return 0;
+ }
+ }
+err_out:
+ msg_perr("Error: cannot synchronize protocol - check communications and reset device?\n");
+ return 1;
+}
+
+static int sp_check_commandavail(uint8_t command)
+{
+ int byteoffs, bitoffs;
+ byteoffs = command / 8;
+ bitoffs = command % 8;
+ return (sp_cmdmap[byteoffs] & (1 << bitoffs)) ? 1 : 0;
+}
+
+static int sp_automatic_cmdcheck(uint8_t cmd)
+{
+ if ((sp_check_avail_automatic) && (sp_check_commandavail(cmd) == 0)) {
+ msg_pdbg("Warning: Automatic command availability check failed "
+ "for cmd 0x%02x - won't execute cmd\n", cmd);
+ return 1;
+ }
+ return 0;
+}
+
+static int sp_docommand(uint8_t command, uint32_t parmlen,
+ uint8_t *params, uint32_t retlen, void *retparms)
+{
+ unsigned char c;
+ if (sp_automatic_cmdcheck(command))
+ return 1;
+ if (serialport_write(&command, 1) != 0) {
+ msg_perr("Error: cannot write op code: %s\n", strerror(errno));
+ return 1;
+ }
+ if (serialport_write(params, parmlen) != 0) {
+ msg_perr("Error: cannot write parameters: %s\n", strerror(errno));
+ return 1;
+ }
+ if (serialport_read(&c, 1) != 0) {
+ msg_perr("Error: cannot read from device: %s\n", strerror(errno));
+ return 1;
+ }
+ if (c == S_NAK)
+ return 1;
+ if (c != S_ACK) {
+ msg_perr("Error: invalid response 0x%02X from device (to command 0x%02X)\n", c, command);
+ return 1;
+ }
+ if (retlen) {
+ if (serialport_read(retparms, retlen) != 0) {
+ msg_perr("Error: cannot read return parameters: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int sp_flush_stream(void)
+{
+ if (sp_streamed_transmit_ops)
+ do {
+ unsigned char c;
+ if (serialport_read(&c, 1) != 0) {
+ msg_perr("Error: cannot read from device (flushing stream)");
+ return 1;
+ }
+ if (c == S_NAK) {
+ msg_perr("Error: NAK to a stream buffer operation\n");
+ return 1;
+ }
+ if (c != S_ACK) {
+ msg_perr("Error: Invalid reply 0x%02X from device\n", c);
+ return 1;
+ }
+ } while (--sp_streamed_transmit_ops);
+ sp_streamed_transmit_ops = 0;
+ sp_streamed_transmit_bytes = 0;
+ return 0;
+}
+
+static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t *parms)
+{
+ uint8_t *sp;
+ if (sp_automatic_cmdcheck(cmd))
+ return 1;
+
+ sp = malloc(1 + parmlen);
+ if (!sp) {
+ msg_perr("Error: cannot malloc command buffer\n");
+ return 1;
+ }
+ sp[0] = cmd;
+ memcpy(&(sp[1]), parms, parmlen);
+
+ if (sp_streamed_transmit_bytes >= (1 + parmlen + sp_device_serbuf_size)) {
+ if (sp_flush_stream() != 0) {
+ free(sp);
+ return 1;
+ }
+ }
+ if (serialport_write(sp, 1 + parmlen) != 0) {
+ msg_perr("Error: cannot write command\n");
+ free(sp);
+ return 1;
+ }
+ sp_streamed_transmit_ops += 1;
+ sp_streamed_transmit_bytes += 1 + parmlen;
+
+ free(sp);
+ return 0;
+}
+
+static int serprog_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+static int serprog_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len);
+static struct spi_master spi_master_serprog = {
+ .type = SPI_CONTROLLER_SERPROG,
+ .max_data_read = MAX_DATA_READ_UNLIMITED,
+ .max_data_write = MAX_DATA_WRITE_UNLIMITED,
+ .command = serprog_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = serprog_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+static void serprog_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr);
+static uint8_t serprog_chip_readb(const struct flashctx *flash,
+ const chipaddr addr);
+static void serprog_chip_readn(const struct flashctx *flash, uint8_t *buf,
+ const chipaddr addr, size_t len);
+static const struct par_master par_master_serprog = {
+ .chip_readb = serprog_chip_readb,
+ .chip_readw = fallback_chip_readw,
+ .chip_readl = fallback_chip_readl,
+ .chip_readn = serprog_chip_readn,
+ .chip_writeb = serprog_chip_writeb,
+ .chip_writew = fallback_chip_writew,
+ .chip_writel = fallback_chip_writel,
+ .chip_writen = fallback_chip_writen,
+};
+
+static enum chipbustype serprog_buses_supported = BUS_NONE;
+
+int serprog_init(void)
+{
+ uint16_t iface;
+ unsigned char pgmname[17];
+ unsigned char rbuf[3];
+ unsigned char c;
+ char *device;
+ int have_device = 0;
+
+ /* the parameter is either of format "dev=/dev/device[:baud]" or "ip=ip:port" */
+ device = extract_programmer_param("dev");
+ if (device && strlen(device)) {
+ char *baud_str = strstr(device, ":");
+ if (baud_str != NULL) {
+ /* Split device from baudrate. */
+ *baud_str = '\0';
+ baud_str++;
+ }
+ int baud;
+ /* Convert baud string to value.
+ * baud_str is either NULL (if strstr can't find the colon), points to the \0 after the colon
+ * if no characters where given after the colon, or a string to convert... */
+ if (baud_str == NULL || *baud_str == '\0') {
+ baud = -1;
+ msg_pdbg("No baudrate specified, using the hardware's defaults.\n");
+ } else
+ baud = atoi(baud_str); // FIXME: replace atoi with strtoul
+ if (strlen(device) > 0) {
+ sp_fd = sp_openserport(device, baud);
+ if (sp_fd == SER_INV_FD) {
+ free(device);
+ return 1;
+ }
+ have_device++;
+ }
+ }
+
+#if !IS_WINDOWS
+ if (device && !strlen(device)) {
+ msg_perr("Error: No device specified.\n"
+ "Use flashrom -p serprog:dev=/dev/device[:baud]\n");
+ free(device);
+ return 1;
+ }
+ free(device);
+
+ device = extract_programmer_param("ip");
+ if (have_device && device) {
+ msg_perr("Error: Both host and device specified.\n"
+ "Please use either dev= or ip= but not both.\n");
+ free(device);
+ return 1;
+ }
+ if (device && strlen(device)) {
+ char *port = strstr(device, ":");
+ if (port != NULL) {
+ /* Split host from port. */
+ *port = '\0';
+ port++;
+ }
+ if (!port || !strlen(port)) {
+ msg_perr("Error: No port specified.\n"
+ "Use flashrom -p serprog:ip=ipaddr:port\n");
+ free(device);
+ return 1;
+ }
+ if (strlen(device)) {
+ sp_fd = sp_opensocket(device, atoi(port)); // FIXME: replace atoi with strtoul
+ if (sp_fd < 0) {
+ free(device);
+ return 1;
+ }
+ have_device++;
+ }
+ }
+ if (device && !strlen(device)) {
+ msg_perr("Error: No host specified.\n"
+ "Use flashrom -p serprog:ip=ipaddr:port\n");
+ free(device);
+ return 1;
+ }
+#endif
+ free(device);
+
+ if (!have_device) {
+#if IS_WINDOWS
+ msg_perr("Error: No device specified.\n"
+ "Use flashrom -p serprog:dev=comN[:baud]\n");
+#else
+ msg_perr("Error: Neither host nor device specified.\n"
+ "Use flashrom -p serprog:dev=/dev/device:baud or "
+ "flashrom -p serprog:ip=ipaddr:port\n");
+#endif
+ return 1;
+ }
+
+ if (register_shutdown(serprog_shutdown, NULL))
+ return 1;
+
+ msg_pdbg(MSGHEADER "connected - attempting to synchronize\n");
+
+ sp_check_avail_automatic = 0;
+
+ if (sp_synchronize())
+ return 1;
+
+ msg_pdbg(MSGHEADER "Synchronized\n");
+
+ if (sp_docommand(S_CMD_Q_IFACE, 0, NULL, 2, &iface)) {
+ msg_perr("Error: NAK to query interface version\n");
+ return 1;
+ }
+
+ if (iface != 1) {
+ msg_perr("Error: Unknown interface version: %d\n", iface);
+ return 1;
+ }
+
+ msg_pdbg(MSGHEADER "Interface version ok.\n");
+
+ if (sp_docommand(S_CMD_Q_CMDMAP, 0, NULL, 32, sp_cmdmap)) {
+ msg_perr("Error: query command map not supported\n");
+ return 1;
+ }
+
+ sp_check_avail_automatic = 1;
+
+ /* FIXME: This assumes that serprog device bustypes are always
+ * identical with flashrom bustype enums and that they all fit
+ * in a single byte.
+ */
+ if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) {
+ msg_pwarn("Warning: NAK to query supported buses\n");
+ c = BUS_NONSPI; /* A reasonable default for now. */
+ }
+ serprog_buses_supported = c;
+
+ msg_pdbg(MSGHEADER "Bus support: parallel=%s, LPC=%s, FWH=%s, SPI=%s\n",
+ (c & BUS_PARALLEL) ? "on" : "off",
+ (c & BUS_LPC) ? "on" : "off",
+ (c & BUS_FWH) ? "on" : "off",
+ (c & BUS_SPI) ? "on" : "off");
+ /* Check for the minimum operational set of commands. */
+ if (serprog_buses_supported & BUS_SPI) {
+ uint8_t bt = BUS_SPI;
+ char *spispeed;
+ if (sp_check_commandavail(S_CMD_O_SPIOP) == 0) {
+ msg_perr("Error: SPI operation not supported while the "
+ "bustype is SPI\n");
+ return 1;
+ }
+ if (sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL))
+ return 1;
+ /* Success of any of these commands is optional. We don't need
+ the programmer to tell us its limits, but if it doesn't, we
+ will assume stuff, so it's in the programmers best interest
+ to tell us. */
+ if (!sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
+ uint32_t v;
+ v = ((unsigned int)(rbuf[0]) << 0);
+ v |= ((unsigned int)(rbuf[1]) << 8);
+ v |= ((unsigned int)(rbuf[2]) << 16);
+ if (v == 0)
+ v = (1 << 24) - 1; /* SPI-op maximum. */
+ spi_master_serprog.max_data_write = v;
+ msg_pdbg(MSGHEADER "Maximum write-n length is %d\n", v);
+ }
+ if (!sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf)) {
+ uint32_t v;
+ v = ((unsigned int)(rbuf[0]) << 0);
+ v |= ((unsigned int)(rbuf[1]) << 8);
+ v |= ((unsigned int)(rbuf[2]) << 16);
+ if (v == 0)
+ v = (1 << 24) - 1; /* SPI-op maximum. */
+ spi_master_serprog.max_data_read = v;
+ msg_pdbg(MSGHEADER "Maximum read-n length is %d\n", v);
+ }
+ spispeed = extract_programmer_param("spispeed");
+ if (spispeed && strlen(spispeed)) {
+ uint32_t f_spi_req, f_spi;
+ uint8_t buf[4];
+ char *f_spi_suffix;
+
+ errno = 0;
+ f_spi_req = strtol(spispeed, &f_spi_suffix, 0);
+ if (errno != 0 || spispeed == f_spi_suffix) {
+ msg_perr("Error: Could not convert 'spispeed'.\n");
+ free(spispeed);
+ return 1;
+ }
+ if (strlen(f_spi_suffix) == 1) {
+ if (!strcasecmp(f_spi_suffix, "M"))
+ f_spi_req *= 1000000;
+ else if (!strcasecmp(f_spi_suffix, "k"))
+ f_spi_req *= 1000;
+ else {
+ msg_perr("Error: Garbage following 'spispeed' value.\n");
+ free(spispeed);
+ return 1;
+ }
+ } else if (strlen(f_spi_suffix) > 1) {
+ msg_perr("Error: Garbage following 'spispeed' value.\n");
+ free(spispeed);
+ return 1;
+ }
+
+ buf[0] = (f_spi_req >> (0 * 8)) & 0xFF;
+ buf[1] = (f_spi_req >> (1 * 8)) & 0xFF;
+ buf[2] = (f_spi_req >> (2 * 8)) & 0xFF;
+ buf[3] = (f_spi_req >> (3 * 8)) & 0xFF;
+
+ if (sp_check_commandavail(S_CMD_S_SPI_FREQ) == 0)
+ msg_pwarn(MSGHEADER "Warning: Setting the SPI clock rate is not supported!\n");
+ else if (sp_docommand(S_CMD_S_SPI_FREQ, 4, buf, 4, buf) == 0) {
+ f_spi = buf[0];
+ f_spi |= buf[1] << (1 * 8);
+ f_spi |= buf[2] << (2 * 8);
+ f_spi |= buf[3] << (3 * 8);
+ msg_pdbg(MSGHEADER "Requested to set SPI clock frequency to %u Hz. "
+ "It was actually set to %u Hz\n", f_spi_req, f_spi);
+ } else
+ msg_pwarn(MSGHEADER "Setting SPI clock rate to %u Hz failed!\n", f_spi_req);
+ }
+ free(spispeed);
+ bt = serprog_buses_supported;
+ if (sp_docommand(S_CMD_S_BUSTYPE, 1, &bt, 0, NULL))
+ return 1;
+ }
+
+ if (serprog_buses_supported & BUS_NONSPI) {
+ if (sp_check_commandavail(S_CMD_O_INIT) == 0) {
+ msg_perr("Error: Initialize operation buffer "
+ "not supported\n");
+ return 1;
+ }
+
+ if (sp_check_commandavail(S_CMD_O_DELAY) == 0) {
+ msg_perr("Error: Write to opbuf: "
+ "delay not supported\n");
+ return 1;
+ }
+
+ /* S_CMD_O_EXEC availability checked later. */
+
+ if (sp_check_commandavail(S_CMD_R_BYTE) == 0) {
+ msg_perr("Error: Single byte read not supported\n");
+ return 1;
+ }
+ /* This could be translated to single byte reads (if missing),
+ * but now we don't support that. */
+ if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) {
+ msg_perr("Error: Read n bytes not supported\n");
+ return 1;
+ }
+ if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) {
+ msg_perr("Error: Write to opbuf: "
+ "write byte not supported\n");
+ return 1;
+ }
+
+ if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
+ msg_pdbg(MSGHEADER "Write-n not supported");
+ sp_max_write_n = 0;
+ } else {
+ sp_max_write_n = ((unsigned int)(rbuf[0]) << 0);
+ sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8);
+ sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16);
+ if (!sp_max_write_n) {
+ sp_max_write_n = (1 << 24);
+ }
+ msg_pdbg(MSGHEADER "Maximum write-n length is %d\n",
+ sp_max_write_n);
+ sp_write_n_buf = malloc(sp_max_write_n);
+ if (!sp_write_n_buf) {
+ msg_perr("Error: cannot allocate memory for "
+ "Write-n buffer\n");
+ return 1;
+ }
+ sp_write_n_bytes = 0;
+ }
+
+ if (sp_check_commandavail(S_CMD_Q_RDNMAXLEN) &&
+ (sp_docommand(S_CMD_Q_RDNMAXLEN, 0, NULL, 3, rbuf) == 0)) {
+ sp_max_read_n = ((unsigned int)(rbuf[0]) << 0);
+ sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8);
+ sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16);
+ msg_pdbg(MSGHEADER "Maximum read-n length is %d\n",
+ sp_max_read_n ? sp_max_read_n : (1 << 24));
+ } else {
+ msg_pdbg(MSGHEADER "Maximum read-n length "
+ "not reported\n");
+ sp_max_read_n = 0;
+ }
+
+ }
+
+ if (sp_docommand(S_CMD_Q_PGMNAME, 0, NULL, 16, pgmname)) {
+ msg_pwarn("Warning: NAK to query programmer name\n");
+ strcpy((char *)pgmname, "(unknown)");
+ }
+ pgmname[16] = 0;
+ msg_pinfo(MSGHEADER "Programmer name is \"%s\"\n", pgmname);
+
+ if (sp_docommand(S_CMD_Q_SERBUF, 0, NULL, 2, &sp_device_serbuf_size)) {
+ msg_pwarn("Warning: NAK to query serial buffer size\n");
+ }
+ msg_pdbg(MSGHEADER "Serial buffer size is %d\n",
+ sp_device_serbuf_size);
+
+ if (sp_check_commandavail(S_CMD_O_INIT)) {
+ /* This would be inconsistent. */
+ if (sp_check_commandavail(S_CMD_O_EXEC) == 0) {
+ msg_perr("Error: Execute operation buffer not "
+ "supported\n");
+ return 1;
+ }
+
+ if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) {
+ msg_perr("Error: NAK to initialize operation buffer\n");
+ return 1;
+ }
+
+ if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2,
+ &sp_device_opbuf_size)) {
+ msg_pwarn("Warning: NAK to query operation buffer size\n");
+ }
+ msg_pdbg(MSGHEADER "operation buffer size is %d\n",
+ sp_device_opbuf_size);
+ }
+
+ if (sp_check_commandavail(S_CMD_S_PIN_STATE)) {
+ uint8_t en = 1;
+ if (sp_docommand(S_CMD_S_PIN_STATE, 1, &en, 0, NULL) != 0) {
+ msg_perr("Error: could not enable output buffers\n");
+ return 1;
+ } else
+ msg_pdbg(MSGHEADER "Output drivers enabled\n");
+ } else
+ msg_pdbg(MSGHEADER "Warning: Programmer does not support toggling its output drivers\n");
+ sp_prev_was_write = 0;
+ sp_streamed_transmit_ops = 0;
+ sp_streamed_transmit_bytes = 0;
+ sp_opbuf_usage = 0;
+ if (serprog_buses_supported & BUS_SPI)
+ register_spi_master(&spi_master_serprog);
+ if (serprog_buses_supported & BUS_NONSPI)
+ register_par_master(&par_master_serprog, serprog_buses_supported & BUS_NONSPI);
+ return 0;
+}
+
+/* Move an in flashrom buffer existing write-n operation to the on-device operation buffer. */
+static int sp_pass_writen(void)
+{
+ unsigned char header[7];
+ msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n", sp_write_n_bytes, sp_write_n_addr);
+ if (sp_streamed_transmit_bytes >= (7 + sp_write_n_bytes + sp_device_serbuf_size)) {
+ if (sp_flush_stream() != 0) {
+ return 1;
+ }
+ }
+ /* In case it's just a single byte send it as a single write. */
+ if (sp_write_n_bytes == 1) {
+ sp_write_n_bytes = 0;
+ header[0] = (sp_write_n_addr >> 0) & 0xFF;
+ header[1] = (sp_write_n_addr >> 8) & 0xFF;
+ header[2] = (sp_write_n_addr >> 16) & 0xFF;
+ header[3] = sp_write_n_buf[0];
+ if (sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header) != 0)
+ return 1;
+ sp_opbuf_usage += 5;
+ return 0;
+ }
+ header[0] = S_CMD_O_WRITEN;
+ header[1] = (sp_write_n_bytes >> 0) & 0xFF;
+ header[2] = (sp_write_n_bytes >> 8) & 0xFF;
+ header[3] = (sp_write_n_bytes >> 16) & 0xFF;
+ header[4] = (sp_write_n_addr >> 0) & 0xFF;
+ header[5] = (sp_write_n_addr >> 8) & 0xFF;
+ header[6] = (sp_write_n_addr >> 16) & 0xFF;
+ if (serialport_write(header, 7) != 0) {
+ msg_perr(MSGHEADER "Error: cannot write write-n command\n");
+ return 1;
+ }
+ if (serialport_write(sp_write_n_buf, sp_write_n_bytes) != 0) {
+ msg_perr(MSGHEADER "Error: cannot write write-n data");
+ return 1;
+ }
+ sp_streamed_transmit_bytes += 7 + sp_write_n_bytes;
+ sp_streamed_transmit_ops += 1;
+ sp_opbuf_usage += 7 + sp_write_n_bytes;
+ sp_write_n_bytes = 0;
+ sp_prev_was_write = 0;
+ return 0;
+}
+
+static int sp_execute_opbuf_noflush(void)
+{
+ if ((sp_max_write_n) && (sp_write_n_bytes)) {
+ if (sp_pass_writen() != 0) {
+ msg_perr("Error: could not transfer write buffer\n");
+ return 1;
+ }
+ }
+ if (sp_stream_buffer_op(S_CMD_O_EXEC, 0, NULL) != 0) {
+ msg_perr("Error: could not execute command buffer\n");
+ return 1;
+ }
+ msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n", sp_opbuf_usage);
+ sp_opbuf_usage = 0;
+ sp_prev_was_write = 0;
+ return 0;
+}
+
+static int sp_execute_opbuf(void)
+{
+ if (sp_execute_opbuf_noflush() != 0)
+ return 1;
+ if (sp_flush_stream() != 0)
+ return 1;
+
+ return 0;
+}
+
+static int serprog_shutdown(void *data)
+{
+ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+ if (sp_execute_opbuf() != 0)
+ msg_pwarn("Could not flush command buffer.\n");
+ if (sp_check_commandavail(S_CMD_S_PIN_STATE)) {
+ uint8_t dis = 0;
+ if (sp_docommand(S_CMD_S_PIN_STATE, 1, &dis, 0, NULL) == 0)
+ msg_pdbg(MSGHEADER "Output drivers disabled\n");
+ else
+ msg_pwarn(MSGHEADER "%s: Warning: could not disable output buffers\n", __func__);
+ }
+ /* FIXME: fix sockets on windows(?), especially closing */
+ serialport_shutdown(&sp_fd);
+ if (sp_max_write_n)
+ free(sp_write_n_buf);
+ return 0;
+}
+
+static int sp_check_opbuf_usage(int bytes_to_be_added)
+{
+ if (sp_device_opbuf_size <= (sp_opbuf_usage + bytes_to_be_added)) {
+ /* If this happens in the middle of a page load the page load will probably fail. */
+ msg_pwarn(MSGHEADER "Warning: executed operation buffer due to size reasons\n");
+ if (sp_execute_opbuf() != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void serprog_chip_writeb(const struct flashctx *flash, uint8_t val,
+ chipaddr addr)
+{
+ msg_pspew("%s\n", __func__);
+ if (sp_max_write_n) {
+ if ((sp_prev_was_write)
+ && (addr == (sp_write_n_addr + sp_write_n_bytes))) {
+ sp_write_n_buf[sp_write_n_bytes++] = val;
+ } else {
+ if ((sp_prev_was_write) && (sp_write_n_bytes))
+ sp_pass_writen();
+ sp_prev_was_write = 1;
+ sp_write_n_addr = addr;
+ sp_write_n_bytes = 1;
+ sp_write_n_buf[0] = val;
+ }
+ sp_check_opbuf_usage(7 + sp_write_n_bytes);
+ if (sp_write_n_bytes >= sp_max_write_n)
+ sp_pass_writen();
+ } else {
+ /* We will have to do single writeb ops. */
+ unsigned char writeb_parm[4];
+ sp_check_opbuf_usage(6);
+ writeb_parm[0] = (addr >> 0) & 0xFF;
+ writeb_parm[1] = (addr >> 8) & 0xFF;
+ writeb_parm[2] = (addr >> 16) & 0xFF;
+ writeb_parm[3] = val;
+ sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm); // FIXME: return error
+ sp_opbuf_usage += 5;
+ }
+}
+
+static uint8_t serprog_chip_readb(const struct flashctx *flash,
+ const chipaddr addr)
+{
+ unsigned char c;
+ unsigned char buf[3];
+ /* Will stream the read operation - eg. add it to the stream buffer, *
+ * then flush the buffer, then read the read answer. */
+ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+ sp_execute_opbuf_noflush();
+ buf[0] = ((addr >> 0) & 0xFF);
+ buf[1] = ((addr >> 8) & 0xFF);
+ buf[2] = ((addr >> 16) & 0xFF);
+ sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf); // FIXME: return error
+ sp_flush_stream(); // FIXME: return error
+ if (serialport_read(&c, 1) != 0)
+ msg_perr(MSGHEADER "readb byteread"); // FIXME: return error
+ msg_pspew("%s addr=0x%" PRIxPTR " returning 0x%02X\n", __func__, addr, c);
+ return c;
+}
+
+/* Local version that really does the job, doesn't care of max_read_n. */
+static int sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len)
+{
+ unsigned char sbuf[6];
+ msg_pspew("%s: addr=0x%" PRIxPTR " len=%zu\n", __func__, addr, len);
+ /* Stream the read-n -- as above. */
+ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+ sp_execute_opbuf_noflush();
+ sbuf[0] = ((addr >> 0) & 0xFF);
+ sbuf[1] = ((addr >> 8) & 0xFF);
+ sbuf[2] = ((addr >> 16) & 0xFF);
+ sbuf[3] = ((len >> 0) & 0xFF);
+ sbuf[4] = ((len >> 8) & 0xFF);
+ sbuf[5] = ((len >> 16) & 0xFF);
+ sp_stream_buffer_op(S_CMD_R_NBYTES, 6, sbuf);
+ if (sp_flush_stream() != 0)
+ return 1;
+ if (serialport_read(buf, len) != 0) {
+ msg_perr(MSGHEADER "Error: cannot read read-n data");
+ return 1;
+ }
+ return 0;
+}
+
+/* The externally called version that makes sure that max_read_n is obeyed. */
+static void serprog_chip_readn(const struct flashctx *flash, uint8_t * buf,
+ const chipaddr addr, size_t len)
+{
+ size_t lenm = len;
+ chipaddr addrm = addr;
+ while ((sp_max_read_n != 0) && (lenm > sp_max_read_n)) {
+ sp_do_read_n(&(buf[addrm-addr]), addrm, sp_max_read_n); // FIXME: return error
+ addrm += sp_max_read_n;
+ lenm -= sp_max_read_n;
+ }
+ if (lenm)
+ sp_do_read_n(&(buf[addrm-addr]), addrm, lenm); // FIXME: return error
+}
+
+void serprog_delay(unsigned int usecs)
+{
+ unsigned char buf[4];
+ msg_pspew("%s usecs=%d\n", __func__, usecs);
+ if (!sp_check_commandavail(S_CMD_O_DELAY)) {
+ msg_pdbg2("serprog_delay used, but programmer doesn't support delays natively - emulating\n");
+ internal_delay(usecs);
+ return;
+ }
+ if ((sp_max_write_n) && (sp_write_n_bytes))
+ sp_pass_writen();
+ sp_check_opbuf_usage(5);
+ buf[0] = ((usecs >> 0) & 0xFF);
+ buf[1] = ((usecs >> 8) & 0xFF);
+ buf[2] = ((usecs >> 16) & 0xFF);
+ buf[3] = ((usecs >> 24) & 0xFF);
+ sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
+ sp_opbuf_usage += 5;
+ sp_prev_was_write = 0;
+}
+
+static int serprog_spi_send_command(struct flashctx *flash,
+ unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ unsigned char *parmbuf;
+ int ret;
+ msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
+ if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes)) {
+ if (sp_execute_opbuf() != 0) {
+ msg_perr("Error: could not execute command buffer before sending SPI commands.\n");
+ return 1;
+ }
+ }
+
+ parmbuf = malloc(writecnt + 6);
+ if (!parmbuf) {
+ msg_perr("Error: could not allocate SPI send param buffer.\n");
+ return 1;
+ }
+ parmbuf[0] = (writecnt >> 0) & 0xFF;
+ parmbuf[1] = (writecnt >> 8) & 0xFF;
+ parmbuf[2] = (writecnt >> 16) & 0xFF;
+ parmbuf[3] = (readcnt >> 0) & 0xFF;
+ parmbuf[4] = (readcnt >> 8) & 0xFF;
+ parmbuf[5] = (readcnt >> 16) & 0xFF;
+ memcpy(parmbuf + 6, writearr, writecnt);
+ ret = sp_docommand(S_CMD_O_SPIOP, writecnt + 6, parmbuf, readcnt,
+ readarr);
+ free(parmbuf);
+ return ret;
+}
+
+/* FIXME: This function is optimized so that it does not split each transaction
+ * into chip page_size long blocks unnecessarily like spi_read_chunked. This has
+ * the advantage that it is much faster for most chips, but breaks those with
+ * non-continuous reads. When spi_read_chunked is fixed this method can be removed. */
+static int serprog_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ unsigned int i, cur_len;
+ const unsigned int max_read = spi_master_serprog.max_data_read;
+ for (i = 0; i < len; i += cur_len) {
+ int ret;
+ cur_len = min(max_read, (len - i));
+ ret = spi_nbyte_read(flash, start + i, buf + i, cur_len);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+void *serprog_map(const char *descr, uintptr_t phys_addr, size_t len)
+{
+ /* Serprog transmits 24 bits only and assumes the underlying implementation handles any remaining bits
+ * correctly (usually setting them to one either in software (for FWH/LPC) or relying on the fact that
+ * the hardware observes a subset of the address bits only). Combined with the standard mapping of
+ * flashrom this creates a 16 MB-wide window just below the 4 GB boundary where serprog can operate (as
+ * needed for non-SPI chips). Below we make sure that the requested range is within this window. */
+ if ((phys_addr & 0xFF000000) == 0xFF000000) {
+ return (void*)phys_addr;
+ } else {
+ msg_pwarn(MSGHEADER "requested mapping %s is incompatible: 0x%zx bytes at 0x%0*" PRIxPTR ".\n",
+ descr, len, PRIxPTR_WIDTH, phys_addr);
+ return NULL;
+ }
+}
diff --git a/serprog.h b/serprog.h
new file mode 100644
index 0000000..b54aaea
--- /dev/null
+++ b/serprog.h
@@ -0,0 +1,25 @@
+/* According to Serial Flasher Protocol Specification - version 1 */
+#define S_ACK 0x06
+#define S_NAK 0x15
+#define S_CMD_NOP 0x00 /* No operation */
+#define S_CMD_Q_IFACE 0x01 /* Query interface version */
+#define S_CMD_Q_CMDMAP 0x02 /* Query supported commands bitmap */
+#define S_CMD_Q_PGMNAME 0x03 /* Query programmer name */
+#define S_CMD_Q_SERBUF 0x04 /* Query Serial Buffer Size */
+#define S_CMD_Q_BUSTYPE 0x05 /* Query supported bustypes */
+#define S_CMD_Q_CHIPSIZE 0x06 /* Query supported chipsize (2^n format) */
+#define S_CMD_Q_OPBUF 0x07 /* Query operation buffer size */
+#define S_CMD_Q_WRNMAXLEN 0x08 /* Query Write to opbuf: Write-N maximum length */
+#define S_CMD_R_BYTE 0x09 /* Read a single byte */
+#define S_CMD_R_NBYTES 0x0A /* Read n bytes */
+#define S_CMD_O_INIT 0x0B /* Initialize operation buffer */
+#define S_CMD_O_WRITEB 0x0C /* Write opbuf: Write byte with address */
+#define S_CMD_O_WRITEN 0x0D /* Write to opbuf: Write-N */
+#define S_CMD_O_DELAY 0x0E /* Write opbuf: udelay */
+#define S_CMD_O_EXEC 0x0F /* Execute operation buffer */
+#define S_CMD_SYNCNOP 0x10 /* Special no-operation that returns NAK+ACK */
+#define S_CMD_Q_RDNMAXLEN 0x11 /* Query read-n maximum length */
+#define S_CMD_S_BUSTYPE 0x12 /* Set used bustype(s). */
+#define S_CMD_O_SPIOP 0x13 /* Perform SPI operation. */
+#define S_CMD_S_SPI_FREQ 0x14 /* Set SPI clock frequency */
+#define S_CMD_S_PIN_STATE 0x15 /* Enable/disable output drivers */
diff --git a/sfdp.c b/sfdp.c
new file mode 100644
index 0000000..bc69dd0
--- /dev/null
+++ b/sfdp.c
@@ -0,0 +1,394 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2011-2012 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "spi.h"
+#include "chipdrivers.h"
+
+static int spi_sfdp_read_sfdp_chunk(struct flashctx *flash, uint32_t address, uint8_t *buf, int len)
+{
+ int i, ret;
+ uint8_t *newbuf;
+ const unsigned char cmd[JEDEC_SFDP_OUTSIZE] = {
+ JEDEC_SFDP,
+ (address >> 16) & 0xff,
+ (address >> 8) & 0xff,
+ (address >> 0) & 0xff,
+ /* FIXME: the following dummy byte explodes on some programmers.
+ * One workaround is to read the dummy byte
+ * instead and discard its value.
+ */
+ 0
+ };
+ msg_cspew("%s: addr=0x%x, len=%d, data:\n", __func__, address, len);
+ newbuf = malloc(len + 1);
+ if (!newbuf)
+ return SPI_PROGRAMMER_ERROR;
+ ret = spi_send_command(flash, sizeof(cmd) - 1, len + 1, cmd, newbuf);
+ memmove(buf, newbuf + 1, len);
+ free(newbuf);
+ if (ret)
+ return ret;
+ for (i = 0; i < len; i++)
+ msg_cspew(" 0x%02x", buf[i]);
+ msg_cspew("\n");
+ return 0;
+}
+
+static int spi_sfdp_read_sfdp(struct flashctx *flash, uint32_t address, uint8_t *buf, int len)
+{
+ /* FIXME: There are different upper bounds for the number of bytes to
+ * read on the various programmers (even depending on the rest of the
+ * structure of the transaction). 2 is a safe bet. */
+ int maxstep = 2;
+ int ret = 0;
+ while (len > 0) {
+ int step = min(len, maxstep);
+ ret = spi_sfdp_read_sfdp_chunk(flash, address, buf, step);
+ if (ret)
+ return ret;
+ address += step;
+ buf += step;
+ len -= step;
+ }
+ return ret;
+}
+
+struct sfdp_tbl_hdr {
+ uint8_t id;
+ uint8_t v_minor;
+ uint8_t v_major;
+ uint8_t len;
+ uint32_t ptp; /* 24b pointer */
+};
+
+static int sfdp_add_uniform_eraser(struct flashchip *chip, uint8_t opcode, uint32_t block_size)
+{
+ int i;
+ uint32_t total_size = chip->total_size * 1024;
+ erasefunc_t *erasefn = spi_get_erasefn_from_opcode(opcode);
+
+ if (erasefn == NULL || total_size == 0 || block_size == 0 ||
+ total_size % block_size != 0) {
+ msg_cdbg("%s: invalid input, please report to "
+ "flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+
+ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
+ struct block_eraser *eraser = &chip->block_erasers[i];
+ /* Check for duplicates (including (some) non-uniform ones). */
+ if (eraser->eraseblocks[0].size == block_size &&
+ eraser->block_erase == erasefn) {
+ msg_cdbg2(" Tried to add a duplicate block eraser: "
+ "%d x %d B with opcode 0x%02x.\n",
+ total_size/block_size, block_size, opcode);
+ return 1;
+ }
+ if (eraser->eraseblocks[0].size != 0 ||
+ eraser->block_erase != NULL) {
+ msg_cspew(" Block Eraser %d is already occupied.\n",
+ i);
+ continue;
+ }
+
+ eraser->block_erase = erasefn;
+ eraser->eraseblocks[0].size = block_size;
+ eraser->eraseblocks[0].count = total_size/block_size;
+ msg_cdbg2(" Block eraser %d: %d x %d B with opcode "
+ "0x%02x\n", i, total_size/block_size, block_size,
+ opcode);
+ return 0;
+ }
+ msg_cinfo("%s: Not enough space to store another eraser (i=%d)."
+ " Please report this at flashrom@flashrom.org\n",
+ __func__, i);
+ return 1;
+}
+
+static int sfdp_fill_flash(struct flashchip *chip, uint8_t *buf, uint16_t len)
+{
+ uint8_t opcode_4k_erase = 0xFF;
+ uint32_t tmp32;
+ uint8_t tmp8;
+ uint32_t total_size; /* in bytes */
+ uint32_t block_size;
+ int j;
+
+ msg_cdbg("Parsing JEDEC flash parameter table... ");
+ if (len != 9 * 4 && len != 4 * 4) {
+ msg_cdbg("%s: len out of spec\n", __func__);
+ return 1;
+ }
+ msg_cdbg2("\n");
+
+ /* 1. double word */
+ tmp32 = ((unsigned int)buf[(4 * 0) + 0]);
+ tmp32 |= ((unsigned int)buf[(4 * 0) + 1]) << 8;
+ tmp32 |= ((unsigned int)buf[(4 * 0) + 2]) << 16;
+ tmp32 |= ((unsigned int)buf[(4 * 0) + 3]) << 24;
+
+ tmp8 = (tmp32 >> 17) & 0x3;
+ switch (tmp8) {
+ case 0x0:
+ msg_cdbg2(" 3-Byte only addressing.\n");
+ break;
+ case 0x1:
+ msg_cdbg2(" 3-Byte (and optionally 4-Byte) addressing.\n");
+ break;
+ case 0x2:
+ msg_cdbg(" 4-Byte only addressing (not supported by "
+ "flashrom).\n");
+ return 1;
+ default:
+ msg_cdbg(" Required addressing mode (0x%x) not supported.\n",
+ tmp8);
+ return 1;
+ }
+
+ msg_cdbg2(" Status register is ");
+ if (tmp32 & (1 << 3)) {
+ msg_cdbg2("volatile and writes to the status register have to "
+ "be enabled with ");
+ if (tmp32 & (1 << 4)) {
+ chip->feature_bits = FEATURE_WRSR_WREN;
+ msg_cdbg2("WREN (0x06).\n");
+ } else {
+ chip->feature_bits = FEATURE_WRSR_EWSR;
+ msg_cdbg2("EWSR (0x50).\n");
+ }
+ } else {
+ msg_cdbg2("non-volatile and the standard does not allow "
+ "vendors to tell us whether EWSR/WREN is needed for "
+ "status register writes - assuming EWSR.\n");
+ chip->feature_bits = FEATURE_WRSR_EWSR;
+ }
+
+ msg_cdbg2(" Write chunk size is ");
+ if (tmp32 & (1 << 2)) {
+ msg_cdbg2("at least 64 B.\n");
+ chip->page_size = 64;
+ chip->write = spi_chip_write_256;
+ } else {
+ msg_cdbg2("1 B only.\n");
+ chip->page_size = 256;
+ chip->write = spi_chip_write_1;
+ }
+
+ if ((tmp32 & 0x3) == 0x1) {
+ opcode_4k_erase = (tmp32 >> 8) & 0xFF;
+ msg_cspew(" 4kB erase opcode is 0x%02x.\n", opcode_4k_erase);
+ /* add the eraser later, because we don't know total_size yet */
+ } else
+ msg_cspew(" 4kB erase opcode is not defined.\n");
+
+ /* 2. double word */
+ tmp32 = ((unsigned int)buf[(4 * 1) + 0]);
+ tmp32 |= ((unsigned int)buf[(4 * 1) + 1]) << 8;
+ tmp32 |= ((unsigned int)buf[(4 * 1) + 2]) << 16;
+ tmp32 |= ((unsigned int)buf[(4 * 1) + 3]) << 24;
+
+ if (tmp32 & (1 << 31)) {
+ msg_cdbg("Flash chip size >= 4 Gb/512 MB not supported.\n");
+ return 1;
+ }
+ total_size = ((tmp32 & 0x7FFFFFFF) + 1) / 8;
+ chip->total_size = total_size / 1024;
+ msg_cdbg2(" Flash chip size is %d kB.\n", chip->total_size);
+ if (total_size > (1 << 24)) {
+ msg_cdbg("Flash chip size is bigger than what 3-Byte addressing "
+ "can access.\n");
+ return 1;
+ }
+
+ if (opcode_4k_erase != 0xFF)
+ sfdp_add_uniform_eraser(chip, opcode_4k_erase, 4 * 1024);
+
+ /* FIXME: double words 3-7 contain unused fast read information */
+
+ if (len == 4 * 4) {
+ msg_cdbg(" It seems like this chip supports the preliminary "
+ "Intel version of SFDP, skipping processing of double "
+ "words 3-9.\n");
+ goto done;
+ }
+
+ /* 8. double word */
+ for (j = 0; j < 4; j++) {
+ /* 7 double words from the start + 2 bytes for every eraser */
+ tmp8 = buf[(4 * 7) + (j * 2)];
+ msg_cspew(" Erase Sector Type %d Size: 0x%02x\n", j + 1,
+ tmp8);
+ if (tmp8 == 0) {
+ msg_cspew(" Erase Sector Type %d is unused.\n", j);
+ continue;
+ }
+ if (tmp8 >= 31) {
+ msg_cdbg2(" Block size of erase Sector Type %d (2^%d) "
+ "is too big for flashrom.\n", j, tmp8);
+ continue;
+ }
+ block_size = 1 << (tmp8); /* block_size = 2 ^ field */
+
+ tmp8 = buf[(4 * 7) + (j * 2) + 1];
+ msg_cspew(" Erase Sector Type %d Opcode: 0x%02x\n", j + 1,
+ tmp8);
+ sfdp_add_uniform_eraser(chip, tmp8, block_size);
+ }
+
+done:
+ msg_cdbg("done.\n");
+ return 0;
+}
+
+int probe_spi_sfdp(struct flashctx *flash)
+{
+ int ret = 0;
+ uint8_t buf[8];
+ uint32_t tmp32;
+ uint8_t nph;
+ /* need to limit the table loop by comparing i to uint8_t nph hence: */
+ uint16_t i;
+ struct sfdp_tbl_hdr *hdrs;
+ uint8_t *hbuf;
+ uint8_t *tbuf;
+
+ if (spi_sfdp_read_sfdp(flash, 0x00, buf, 4)) {
+ msg_cdbg("Receiving SFDP signature failed.\n");
+ return 0;
+ }
+ tmp32 = buf[0];
+ tmp32 |= ((unsigned int)buf[1]) << 8;
+ tmp32 |= ((unsigned int)buf[2]) << 16;
+ tmp32 |= ((unsigned int)buf[3]) << 24;
+
+ if (tmp32 != 0x50444653) {
+ msg_cdbg2("Signature = 0x%08x (should be 0x50444653)\n", tmp32);
+ msg_cdbg("No SFDP signature found.\n");
+ return 0;
+ }
+
+ if (spi_sfdp_read_sfdp(flash, 0x04, buf, 3)) {
+ msg_cdbg("Receiving SFDP revision and number of parameter "
+ "headers (NPH) failed. ");
+ return 0;
+ }
+ msg_cdbg2("SFDP revision = %d.%d\n", buf[1], buf[0]);
+ if (buf[1] != 0x01) {
+ msg_cdbg("The chip supports an unknown version of SFDP. "
+ "Aborting SFDP probe!\n");
+ return 0;
+ }
+ nph = buf[2];
+ msg_cdbg2("SFDP number of parameter headers is %d (NPH = %d).\n",
+ nph + 1, nph);
+
+ /* Fetch all parameter headers, even if we don't use them all (yet). */
+ hbuf = malloc((nph + 1) * 8);
+ hdrs = malloc((nph + 1) * sizeof(struct sfdp_tbl_hdr));
+ if (hbuf == NULL || hdrs == NULL ) {
+ msg_gerr("Out of memory!\n");
+ goto cleanup_hdrs;
+ }
+ if (spi_sfdp_read_sfdp(flash, 0x08, hbuf, (nph + 1) * 8)) {
+ msg_cdbg("Receiving SFDP parameter table headers failed.\n");
+ goto cleanup_hdrs;
+ }
+
+ for (i = 0; i <= nph; i++) {
+ uint16_t len;
+ hdrs[i].id = hbuf[(8 * i) + 0];
+ hdrs[i].v_minor = hbuf[(8 * i) + 1];
+ hdrs[i].v_major = hbuf[(8 * i) + 2];
+ hdrs[i].len = hbuf[(8 * i) + 3];
+ hdrs[i].ptp = hbuf[(8 * i) + 4];
+ hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 5]) << 8;
+ hdrs[i].ptp |= ((unsigned int)hbuf[(8 * i) + 6]) << 16;
+ msg_cdbg2("\nSFDP parameter table header %d/%d:\n", i, nph);
+ msg_cdbg2(" ID 0x%02x, version %d.%d\n", hdrs[i].id,
+ hdrs[i].v_major, hdrs[i].v_minor);
+ len = hdrs[i].len * 4;
+ tmp32 = hdrs[i].ptp;
+ msg_cdbg2(" Length %d B, Parameter Table Pointer 0x%06x\n",
+ len, tmp32);
+
+ if (tmp32 + len >= (1 << 24)) {
+ msg_cdbg("SFDP Parameter Table %d supposedly overflows "
+ "addressable SFDP area. This most\nprobably "
+ "indicates a corrupt SFDP parameter table "
+ "header. Skipping it.\n", i);
+ continue;
+ }
+
+ tbuf = malloc(len);
+ if (tbuf == NULL) {
+ msg_gerr("Out of memory!\n");
+ goto cleanup_hdrs;
+ }
+ if (spi_sfdp_read_sfdp(flash, tmp32, tbuf, len)){
+ msg_cdbg("Fetching SFDP parameter table %d failed.\n",
+ i);
+ free(tbuf);
+ continue;
+ }
+ msg_cspew(" Parameter table contents:\n");
+ for (tmp32 = 0; tmp32 < len; tmp32++) {
+ if ((tmp32 % 8) == 0) {
+ msg_cspew(" 0x%04x: ", tmp32);
+ }
+ msg_cspew(" %02x", tbuf[tmp32]);
+ if ((tmp32 % 8) == 7) {
+ msg_cspew("\n");
+ continue;
+ }
+ if ((tmp32 % 8) == 3) {
+ msg_cspew(" ");
+ continue;
+ }
+ }
+ msg_cspew("\n");
+
+ if (i == 0) { /* Mandatory JEDEC SFDP parameter table */
+ if (hdrs[i].id != 0)
+ msg_cdbg("ID of the mandatory JEDEC SFDP "
+ "parameter table is not 0 as demanded "
+ "by JESD216 (warning only).\n");
+
+ if (hdrs[i].v_major != 0x01) {
+ msg_cdbg("The chip contains an unknown "
+ "version of the JEDEC flash "
+ "parameters table, skipping it.\n");
+ } else if (len != 9 * 4 && len != 4 * 4) {
+ msg_cdbg("Length of the mandatory JEDEC SFDP "
+ "parameter table is wrong (%d B), "
+ "skipping it.\n", len);
+ } else if (sfdp_fill_flash(flash->chip, tbuf, len) == 0)
+ ret = 1;
+ }
+ free(tbuf);
+ }
+
+cleanup_hdrs:
+ free(hdrs);
+ free(hbuf);
+ return ret;
+}
diff --git a/spi.c b/spi.c
new file mode 100644
index 0000000..894f73f
--- /dev/null
+++ b/spi.c
@@ -0,0 +1,186 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the generic SPI framework
+ */
+
+#include <strings.h>
+#include <string.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+int spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt, const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ return flash->mst->spi.command(flash, writecnt, readcnt, writearr,
+ readarr);
+}
+
+int spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds)
+{
+ return flash->mst->spi.multicommand(flash, cmds);
+}
+
+int default_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ struct spi_command cmd[] = {
+ {
+ .writecnt = writecnt,
+ .readcnt = readcnt,
+ .writearr = writearr,
+ .readarr = readarr,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ return spi_send_multicommand(flash, cmd);
+}
+
+int default_spi_send_multicommand(struct flashctx *flash,
+ struct spi_command *cmds)
+{
+ int result = 0;
+ for (; (cmds->writecnt || cmds->readcnt) && !result; cmds++) {
+ result = spi_send_command(flash, cmds->writecnt, cmds->readcnt,
+ cmds->writearr, cmds->readarr);
+ }
+ return result;
+}
+
+int default_spi_read(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ unsigned int len)
+{
+ unsigned int max_data = flash->mst->spi.max_data_read;
+ if (max_data == MAX_DATA_UNSPECIFIED) {
+ msg_perr("%s called, but SPI read chunk size not defined "
+ "on this hardware. Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+ return spi_read_chunked(flash, buf, start, len, max_data);
+}
+
+int default_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int max_data = flash->mst->spi.max_data_write;
+ if (max_data == MAX_DATA_UNSPECIFIED) {
+ msg_perr("%s called, but SPI write chunk size not defined "
+ "on this hardware. Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ return 1;
+ }
+ return spi_write_chunked(flash, buf, start, len, max_data);
+}
+
+int spi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ unsigned int len)
+{
+ unsigned int addrbase = 0;
+
+ /* Check if the chip fits between lowest valid and highest possible
+ * address. Highest possible address with the current SPI implementation
+ * means 0xffffff, the highest unsigned 24bit number.
+ */
+ addrbase = spi_get_valid_read_addr(flash);
+ if (addrbase + flash->chip->total_size * 1024 > (1 << 24)) {
+ msg_perr("Flash chip size exceeds the allowed access window. ");
+ msg_perr("Read will probably fail.\n");
+ /* Try to get the best alignment subject to constraints. */
+ addrbase = (1 << 24) - flash->chip->total_size * 1024;
+ }
+ /* Check if alignment is native (at least the largest power of two which
+ * is a factor of the mapped size of the chip).
+ */
+ if (ffs(flash->chip->total_size * 1024) > (ffs(addrbase) ? : 33)) {
+ msg_perr("Flash chip is not aligned natively in the allowed "
+ "access window.\n");
+ msg_perr("Read will probably return garbage.\n");
+ }
+ return flash->mst->spi.read(flash, buf, addrbase + start, len);
+}
+
+/*
+ * Program chip using page (256 bytes) programming.
+ * Some SPI masters can't do this, they use single byte programming instead.
+ * The redirect to single byte programming is achieved by setting
+ * .write_256 = spi_chip_write_1
+ */
+/* real chunksize is up to 256, logical chunksize is 256 */
+int spi_chip_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return flash->mst->spi.write_256(flash, buf, start, len);
+}
+
+/*
+ * Get the lowest allowed address for read accesses. This often happens to
+ * be the lowest allowed address for all commands which take an address.
+ * This is a master limitation.
+ */
+uint32_t spi_get_valid_read_addr(struct flashctx *flash)
+{
+ switch (flash->mst->spi.type) {
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+ case SPI_CONTROLLER_ICH7:
+ case SPI_CONTROLLER_ICH9:
+ /* Return BBAR for ICH chipsets. */
+ return ichspi_bbar;
+#endif
+#endif
+ default:
+ return 0;
+ }
+}
+
+int spi_aai_write(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ return flash->mst->spi.write_aai(flash, buf, start, len);
+}
+
+int register_spi_master(const struct spi_master *mst)
+{
+ struct registered_master rmst;
+
+ if (!mst->write_aai || !mst->write_256 || !mst->read || !mst->command ||
+ !mst->multicommand ||
+ ((mst->command == default_spi_send_command) &&
+ (mst->multicommand == default_spi_send_multicommand))) {
+ msg_perr("%s called with incomplete master definition. "
+ "Please report a bug at flashrom@flashrom.org\n",
+ __func__);
+ return ERROR_FLASHROM_BUG;
+ }
+
+
+ rmst.buses_supported = BUS_SPI;
+ rmst.spi = *mst;
+ return register_master(&rmst);
+}
diff --git a/spi.h b/spi.h
new file mode 100644
index 0000000..de5b3be
--- /dev/null
+++ b/spi.h
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __SPI_H__
+#define __SPI_H__ 1
+
+/*
+ * Contains the generic SPI headers
+ */
+
+/* Read Electronic ID */
+#define JEDEC_RDID 0x9f
+#define JEDEC_RDID_OUTSIZE 0x01
+/* INSIZE may be 0x04 for some chips*/
+#define JEDEC_RDID_INSIZE 0x03
+
+/* Some Atmel AT25F* models have bit 3 as don't care bit in commands */
+#define AT25F_RDID 0x15 /* 0x15 or 0x1d */
+#define AT25F_RDID_OUTSIZE 0x01
+#define AT25F_RDID_INSIZE 0x02
+
+/* Read Electronic Manufacturer Signature */
+#define JEDEC_REMS 0x90
+#define JEDEC_REMS_OUTSIZE 0x04
+#define JEDEC_REMS_INSIZE 0x02
+
+/* Read Serial Flash Discoverable Parameters (SFDP) */
+#define JEDEC_SFDP 0x5a
+#define JEDEC_SFDP_OUTSIZE 0x05 /* 8b op, 24b addr, 8b dummy */
+/* JEDEC_SFDP_INSIZE : any length */
+
+/* Read Electronic Signature */
+#define JEDEC_RES 0xab
+#define JEDEC_RES_OUTSIZE 0x04
+/* INSIZE may be 0x02 for some chips*/
+#define JEDEC_RES_INSIZE 0x01
+
+/* Write Enable */
+#define JEDEC_WREN 0x06
+#define JEDEC_WREN_OUTSIZE 0x01
+#define JEDEC_WREN_INSIZE 0x00
+
+/* Write Disable */
+#define JEDEC_WRDI 0x04
+#define JEDEC_WRDI_OUTSIZE 0x01
+#define JEDEC_WRDI_INSIZE 0x00
+
+/* Chip Erase 0x60 is supported by Macronix/SST chips. */
+#define JEDEC_CE_60 0x60
+#define JEDEC_CE_60_OUTSIZE 0x01
+#define JEDEC_CE_60_INSIZE 0x00
+
+/* Chip Erase 0x62 is supported by Atmel AT25F chips. */
+#define JEDEC_CE_62 0x62
+#define JEDEC_CE_62_OUTSIZE 0x01
+#define JEDEC_CE_62_INSIZE 0x00
+
+/* Chip Erase 0xc7 is supported by SST/ST/EON/Macronix chips. */
+#define JEDEC_CE_C7 0xc7
+#define JEDEC_CE_C7_OUTSIZE 0x01
+#define JEDEC_CE_C7_INSIZE 0x00
+
+/* Block Erase 0x50 is supported by Atmel AT26DF chips. */
+#define JEDEC_BE_50 0x50
+#define JEDEC_BE_50_OUTSIZE 0x04
+#define JEDEC_BE_50_INSIZE 0x00
+
+/* Block Erase 0x52 is supported by SST and old Atmel chips. */
+#define JEDEC_BE_52 0x52
+#define JEDEC_BE_52_OUTSIZE 0x04
+#define JEDEC_BE_52_INSIZE 0x00
+
+/* Block Erase 0x81 is supported by Atmel AT26DF chips. */
+#define JEDEC_BE_81 0x81
+#define JEDEC_BE_81_OUTSIZE 0x04
+#define JEDEC_BE_81_INSIZE 0x00
+
+/* Block Erase 0xc4 is supported by Micron chips. */
+#define JEDEC_BE_C4 0xc4
+#define JEDEC_BE_C4_OUTSIZE 0x04
+#define JEDEC_BE_C4_INSIZE 0x00
+
+/* Block Erase 0xd8 is supported by EON/Macronix chips. */
+#define JEDEC_BE_D8 0xd8
+#define JEDEC_BE_D8_OUTSIZE 0x04
+#define JEDEC_BE_D8_INSIZE 0x00
+
+/* Block Erase 0xd7 is supported by PMC chips. */
+#define JEDEC_BE_D7 0xd7
+#define JEDEC_BE_D7_OUTSIZE 0x04
+#define JEDEC_BE_D7_INSIZE 0x00
+
+/* Sector Erase 0x20 is supported by Macronix/SST chips. */
+#define JEDEC_SE 0x20
+#define JEDEC_SE_OUTSIZE 0x04
+#define JEDEC_SE_INSIZE 0x00
+
+/* Page Erase 0xDB */
+#define JEDEC_PE 0xDB
+#define JEDEC_PE_OUTSIZE 0x04
+#define JEDEC_PE_INSIZE 0x00
+
+/* Read Status Register */
+#define JEDEC_RDSR 0x05
+#define JEDEC_RDSR_OUTSIZE 0x01
+#define JEDEC_RDSR_INSIZE 0x01
+
+/* Status Register Bits */
+#define SPI_SR_WIP (0x01 << 0)
+#define SPI_SR_WEL (0x01 << 1)
+#define SPI_SR_AAI (0x01 << 6)
+
+/* Write Status Enable */
+#define JEDEC_EWSR 0x50
+#define JEDEC_EWSR_OUTSIZE 0x01
+#define JEDEC_EWSR_INSIZE 0x00
+
+/* Write Status Register */
+#define JEDEC_WRSR 0x01
+#define JEDEC_WRSR_OUTSIZE 0x02
+#define JEDEC_WRSR_INSIZE 0x00
+
+/* Read the memory */
+#define JEDEC_READ 0x03
+#define JEDEC_READ_OUTSIZE 0x04
+/* JEDEC_READ_INSIZE : any length */
+
+/* Write memory byte */
+#define JEDEC_BYTE_PROGRAM 0x02
+#define JEDEC_BYTE_PROGRAM_OUTSIZE 0x05
+#define JEDEC_BYTE_PROGRAM_INSIZE 0x00
+
+/* Write AAI word (SST25VF080B) */
+#define JEDEC_AAI_WORD_PROGRAM 0xad
+#define JEDEC_AAI_WORD_PROGRAM_OUTSIZE 0x06
+#define JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE 0x03
+#define JEDEC_AAI_WORD_PROGRAM_INSIZE 0x00
+
+/* Error codes */
+#define SPI_GENERIC_ERROR -1
+#define SPI_INVALID_OPCODE -2
+#define SPI_INVALID_ADDRESS -3
+#define SPI_INVALID_LENGTH -4
+#define SPI_FLASHROM_BUG -5
+#define SPI_PROGRAMMER_ERROR -6
+
+#endif /* !__SPI_H__ */
diff --git a/spi25.c b/spi25.c
new file mode 100644
index 0000000..af4b6db
--- /dev/null
+++ b/spi25.c
@@ -0,0 +1,1173 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008, 2009, 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Contains the common SPI chip driver functions
+ */
+
+#include <string.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+static int spi_rdid(struct flashctx *flash, unsigned char *readarr, int bytes)
+{
+ static const unsigned char cmd[JEDEC_RDID_OUTSIZE] = { JEDEC_RDID };
+ int ret;
+ int i;
+
+ ret = spi_send_command(flash, sizeof(cmd), bytes, cmd, readarr);
+ if (ret)
+ return ret;
+ msg_cspew("RDID returned");
+ for (i = 0; i < bytes; i++)
+ msg_cspew(" 0x%02x", readarr[i]);
+ msg_cspew(". ");
+ return 0;
+}
+
+static int spi_rems(struct flashctx *flash, unsigned char *readarr)
+{
+ unsigned char cmd[JEDEC_REMS_OUTSIZE] = { JEDEC_REMS, 0, 0, 0 };
+ uint32_t readaddr;
+ int ret;
+
+ ret = spi_send_command(flash, sizeof(cmd), JEDEC_REMS_INSIZE, cmd,
+ readarr);
+ if (ret == SPI_INVALID_ADDRESS) {
+ /* Find the lowest even address allowed for reads. */
+ readaddr = (spi_get_valid_read_addr(flash) + 1) & ~1;
+ cmd[1] = (readaddr >> 16) & 0xff,
+ cmd[2] = (readaddr >> 8) & 0xff,
+ cmd[3] = (readaddr >> 0) & 0xff,
+ ret = spi_send_command(flash, sizeof(cmd), JEDEC_REMS_INSIZE,
+ cmd, readarr);
+ }
+ if (ret)
+ return ret;
+ msg_cspew("REMS returned 0x%02x 0x%02x. ", readarr[0], readarr[1]);
+ return 0;
+}
+
+static int spi_res(struct flashctx *flash, unsigned char *readarr, int bytes)
+{
+ unsigned char cmd[JEDEC_RES_OUTSIZE] = { JEDEC_RES, 0, 0, 0 };
+ uint32_t readaddr;
+ int ret;
+ int i;
+
+ ret = spi_send_command(flash, sizeof(cmd), bytes, cmd, readarr);
+ if (ret == SPI_INVALID_ADDRESS) {
+ /* Find the lowest even address allowed for reads. */
+ readaddr = (spi_get_valid_read_addr(flash) + 1) & ~1;
+ cmd[1] = (readaddr >> 16) & 0xff,
+ cmd[2] = (readaddr >> 8) & 0xff,
+ cmd[3] = (readaddr >> 0) & 0xff,
+ ret = spi_send_command(flash, sizeof(cmd), bytes, cmd, readarr);
+ }
+ if (ret)
+ return ret;
+ msg_cspew("RES returned");
+ for (i = 0; i < bytes; i++)
+ msg_cspew(" 0x%02x", readarr[i]);
+ msg_cspew(". ");
+ return 0;
+}
+
+int spi_write_enable(struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_WREN_OUTSIZE] = { JEDEC_WREN };
+ int result;
+
+ /* Send WREN (Write Enable) */
+ result = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+
+ if (result)
+ msg_cerr("%s failed\n", __func__);
+
+ return result;
+}
+
+int spi_write_disable(struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_WRDI_OUTSIZE] = { JEDEC_WRDI };
+
+ /* Send WRDI (Write Disable) */
+ return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+}
+
+static int probe_spi_rdid_generic(struct flashctx *flash, int bytes)
+{
+ const struct flashchip *chip = flash->chip;
+ unsigned char readarr[4];
+ uint32_t id1;
+ uint32_t id2;
+
+ if (spi_rdid(flash, readarr, bytes)) {
+ return 0;
+ }
+
+ if (!oddparity(readarr[0]))
+ msg_cdbg("RDID byte 0 parity violation. ");
+
+ /* Check if this is a continuation vendor ID.
+ * FIXME: Handle continuation device IDs.
+ */
+ if (readarr[0] == 0x7f) {
+ if (!oddparity(readarr[1]))
+ msg_cdbg("RDID byte 1 parity violation. ");
+ id1 = (readarr[0] << 8) | readarr[1];
+ id2 = readarr[2];
+ if (bytes > 3) {
+ id2 <<= 8;
+ id2 |= readarr[3];
+ }
+ } else {
+ id1 = readarr[0];
+ id2 = (readarr[1] << 8) | readarr[2];
+ }
+
+ msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+ if (id1 == chip->manufacture_id && id2 == chip->model_id)
+ return 1;
+
+ /* Test if this is a pure vendor match. */
+ if (id1 == chip->manufacture_id && GENERIC_DEVICE_ID == chip->model_id)
+ return 1;
+
+ /* Test if there is any vendor ID. */
+ if (GENERIC_MANUF_ID == chip->manufacture_id && id1 != 0xff && id1 != 0x00)
+ return 1;
+
+ return 0;
+}
+
+int probe_spi_rdid(struct flashctx *flash)
+{
+ return probe_spi_rdid_generic(flash, 3);
+}
+
+int probe_spi_rdid4(struct flashctx *flash)
+{
+ /* Some SPI controllers do not support commands with writecnt=1 and
+ * readcnt=4.
+ */
+ switch (flash->mst->spi.type) {
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+ case SPI_CONTROLLER_IT87XX:
+ case SPI_CONTROLLER_WBSIO:
+ msg_cinfo("4 byte RDID not supported on this SPI controller\n");
+ return 0;
+ break;
+#endif
+#endif
+ default:
+ return probe_spi_rdid_generic(flash, 4);
+ }
+
+ return 0;
+}
+
+int probe_spi_rems(struct flashctx *flash)
+{
+ const struct flashchip *chip = flash->chip;
+ unsigned char readarr[JEDEC_REMS_INSIZE];
+ uint32_t id1, id2;
+
+ if (spi_rems(flash, readarr)) {
+ return 0;
+ }
+
+ id1 = readarr[0];
+ id2 = readarr[1];
+
+ msg_cdbg("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2);
+
+ if (id1 == chip->manufacture_id && id2 == chip->model_id)
+ return 1;
+
+ /* Test if this is a pure vendor match. */
+ if (id1 == chip->manufacture_id && GENERIC_DEVICE_ID == chip->model_id)
+ return 1;
+
+ /* Test if there is any vendor ID. */
+ if (GENERIC_MANUF_ID == chip->manufacture_id && id1 != 0xff && id1 != 0x00)
+ return 1;
+
+ return 0;
+}
+
+int probe_spi_res1(struct flashctx *flash)
+{
+ static const unsigned char allff[] = {0xff, 0xff, 0xff};
+ static const unsigned char all00[] = {0x00, 0x00, 0x00};
+ unsigned char readarr[3];
+ uint32_t id2;
+
+ /* We only want one-byte RES if RDID and REMS are unusable. */
+
+ /* Check if RDID is usable and does not return 0xff 0xff 0xff or
+ * 0x00 0x00 0x00. In that case, RES is pointless.
+ */
+ if (!spi_rdid(flash, readarr, 3) && memcmp(readarr, allff, 3) &&
+ memcmp(readarr, all00, 3)) {
+ msg_cdbg("Ignoring RES in favour of RDID.\n");
+ return 0;
+ }
+ /* Check if REMS is usable and does not return 0xff 0xff or
+ * 0x00 0x00. In that case, RES is pointless.
+ */
+ if (!spi_rems(flash, readarr) &&
+ memcmp(readarr, allff, JEDEC_REMS_INSIZE) &&
+ memcmp(readarr, all00, JEDEC_REMS_INSIZE)) {
+ msg_cdbg("Ignoring RES in favour of REMS.\n");
+ return 0;
+ }
+
+ if (spi_res(flash, readarr, 1)) {
+ return 0;
+ }
+
+ id2 = readarr[0];
+
+ msg_cdbg("%s: id 0x%x\n", __func__, id2);
+
+ if (id2 != flash->chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+int probe_spi_res2(struct flashctx *flash)
+{
+ unsigned char readarr[2];
+ uint32_t id1, id2;
+
+ if (spi_res(flash, readarr, 2)) {
+ return 0;
+ }
+
+ id1 = readarr[0];
+ id2 = readarr[1];
+
+ msg_cdbg("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2);
+
+ if (id1 != flash->chip->manufacture_id || id2 != flash->chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+int probe_spi_res3(struct flashctx *flash)
+{
+ unsigned char readarr[3];
+ uint32_t id1, id2;
+
+ if (spi_res(flash, readarr, 3)) {
+ return 0;
+ }
+
+ id1 = (readarr[0] << 8) | readarr[1];
+ id2 = readarr[2];
+
+ msg_cdbg("%s: id1 0x%x, id2 0x%x\n", __func__, id1, id2);
+
+ if (id1 != flash->chip->manufacture_id || id2 != flash->chip->model_id)
+ return 0;
+
+ return 1;
+}
+
+/* Only used for some Atmel chips. */
+int probe_spi_at25f(struct flashctx *flash)
+{
+ static const unsigned char cmd[AT25F_RDID_OUTSIZE] = { AT25F_RDID };
+ unsigned char readarr[AT25F_RDID_INSIZE];
+ uint32_t id1;
+ uint32_t id2;
+
+ if (spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr))
+ return 0;
+
+ id1 = readarr[0];
+ id2 = readarr[1];
+
+ msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+ if (id1 == flash->chip->manufacture_id && id2 == flash->chip->model_id)
+ return 1;
+
+ return 0;
+}
+
+int spi_chip_erase_60(struct flashctx *flash)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_CE_60_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_CE_60 },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n",
+ __func__);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 1-85 s, so wait in 1 s steps.
+ */
+ /* FIXME: We assume spi_read_status_register will never fail. */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1000 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_chip_erase_62(struct flashctx *flash)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_CE_62_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_CE_62 },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n",
+ __func__);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 2-5 s, so wait in 100 ms steps.
+ */
+ /* FIXME: We assume spi_read_status_register will never fail. */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_chip_erase_c7(struct flashctx *flash)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_CE_C7_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_CE_C7 },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n", __func__);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 1-85 s, so wait in 1 s steps.
+ */
+ /* FIXME: We assume spi_read_status_register will never fail. */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1000 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_block_erase_52(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_52_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_52,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Block size is usually
+ * 32M (one die) for Micron
+ */
+int spi_block_erase_c4(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_C4_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_C4,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n", __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 240-480 s, so wait in 500 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(500 * 1000 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Block size is usually
+ * 64k for Macronix
+ * 32k for SST
+ * 4-32k non-uniform for EON
+ */
+int spi_block_erase_d8(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_D8_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_D8,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Block size is usually
+ * 4k for PMC
+ */
+int spi_block_erase_d7(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BE_D7_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_D7,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 100-4000 ms, so wait in 100 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(100 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Page erase (usually 256B blocks) */
+int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_PE_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_PE,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ } };
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n", __func__, addr);
+ return result;
+ }
+
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This takes up to 20 ms usually (on worn out devices up to the 0.5s range), so wait in 1 ms steps. */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* Sector size is usually 4k, though Macronix eliteflash has 64k */
+int spi_block_erase_20(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_SE_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_SE,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 15-800 ms, so wait in 10 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_block_erase_50(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+/* .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, { */
+ .writecnt = JEDEC_BE_50_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_50,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n", __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 10 ms, so wait in 1 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_block_erase_81(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+/* .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, { */
+ .writecnt = JEDEC_BE_81_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BE_81,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff)
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n", __func__, addr);
+ return result;
+ }
+ /* Wait until the Write-In-Progress bit is cleared.
+ * This usually takes 8 ms, so wait in 1 ms steps.
+ */
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(1 * 1000);
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int spi_block_erase_60(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ if ((addr != 0) || (blocklen != flash->chip->total_size * 1024)) {
+ msg_cerr("%s called with incorrect arguments\n",
+ __func__);
+ return -1;
+ }
+ return spi_chip_erase_60(flash);
+}
+
+int spi_block_erase_62(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+ if ((addr != 0) || (blocklen != flash->chip->total_size * 1024)) {
+ msg_cerr("%s called with incorrect arguments\n",
+ __func__);
+ return -1;
+ }
+ return spi_chip_erase_62(flash);
+}
+
+int spi_block_erase_c7(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ if ((addr != 0) || (blocklen != flash->chip->total_size * 1024)) {
+ msg_cerr("%s called with incorrect arguments\n",
+ __func__);
+ return -1;
+ }
+ return spi_chip_erase_c7(flash);
+}
+
+erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode)
+{
+ switch(opcode){
+ case 0xff:
+ case 0x00:
+ /* Not specified, assuming "not supported". */
+ return NULL;
+ case 0x20:
+ return &spi_block_erase_20;
+ case 0x50:
+ return &spi_block_erase_50;
+ case 0x52:
+ return &spi_block_erase_52;
+ case 0x60:
+ return &spi_block_erase_60;
+ case 0x62:
+ return &spi_block_erase_62;
+ case 0x81:
+ return &spi_block_erase_81;
+ case 0xc4:
+ return &spi_block_erase_c4;
+ case 0xc7:
+ return &spi_block_erase_c7;
+ case 0xd7:
+ return &spi_block_erase_d7;
+ case 0xd8:
+ return &spi_block_erase_d8;
+ case 0xdb:
+ return &spi_block_erase_db;
+ default:
+ msg_cinfo("%s: unknown erase opcode (0x%02x). Please report "
+ "this at flashrom@flashrom.org\n", __func__, opcode);
+ return NULL;
+ }
+}
+
+int spi_byte_program(struct flashctx *flash, unsigned int addr,
+ uint8_t databyte)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ databyte
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len)
+{
+ int result;
+ /* FIXME: Switch to malloc based on len unless that kills speed. */
+ unsigned char cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + 256] = {
+ JEDEC_BYTE_PROGRAM,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff,
+ };
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + len,
+ .writearr = cmd,
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ if (!len) {
+ msg_cerr("%s called for zero-length write\n", __func__);
+ return 1;
+ }
+ if (len > 256) {
+ msg_cerr("%s called for too long a write\n", __func__);
+ return 1;
+ }
+
+ memcpy(&cmd[4], bytes, len);
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ }
+ return result;
+}
+
+int spi_nbyte_read(struct flashctx *flash, unsigned int address, uint8_t *bytes,
+ unsigned int len)
+{
+ const unsigned char cmd[JEDEC_READ_OUTSIZE] = {
+ JEDEC_READ,
+ (address >> 16) & 0xff,
+ (address >> 8) & 0xff,
+ (address >> 0) & 0xff,
+ };
+
+ /* Send Read */
+ return spi_send_command(flash, sizeof(cmd), len, cmd, bytes);
+}
+
+/*
+ * Read a part of the flash chip.
+ * FIXME: Use the chunk code from Michael Karcher instead.
+ * Each page is read separately in chunks with a maximum size of chunksize.
+ */
+int spi_read_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ unsigned int len, unsigned int chunksize)
+{
+ int rc = 0;
+ unsigned int i, j, starthere, lenhere, toread;
+ unsigned int page_size = flash->chip->page_size;
+
+ /* Warning: This loop has a very unusual condition and body.
+ * The loop needs to go through each page with at least one affected
+ * byte. The lowest page number is (start / page_size) since that
+ * division rounds down. The highest page number we want is the page
+ * where the last byte of the range lives. That last byte has the
+ * address (start + len - 1), thus the highest page number is
+ * (start + len - 1) / page_size. Since we want to include that last
+ * page as well, the loop condition uses <=.
+ */
+ for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+ /* Byte position of the first byte in the range in this page. */
+ /* starthere is an offset to the base address of the chip. */
+ starthere = max(start, i * page_size);
+ /* Length of bytes in the range in this page. */
+ lenhere = min(start + len, (i + 1) * page_size) - starthere;
+ for (j = 0; j < lenhere; j += chunksize) {
+ toread = min(chunksize, lenhere - j);
+ rc = spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread);
+ if (rc)
+ break;
+ }
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Write a part of the flash chip.
+ * FIXME: Use the chunk code from Michael Karcher instead.
+ * Each page is written separately in chunks with a maximum size of chunksize.
+ */
+int spi_write_chunked(struct flashctx *flash, const uint8_t *buf, unsigned int start,
+ unsigned int len, unsigned int chunksize)
+{
+ int rc = 0;
+ unsigned int i, j, starthere, lenhere, towrite;
+ /* FIXME: page_size is the wrong variable. We need max_writechunk_size
+ * in struct flashctx to do this properly. All chips using
+ * spi_chip_write_256 have page_size set to max_writechunk_size, so
+ * we're OK for now.
+ */
+ unsigned int page_size = flash->chip->page_size;
+
+ /* Warning: This loop has a very unusual condition and body.
+ * The loop needs to go through each page with at least one affected
+ * byte. The lowest page number is (start / page_size) since that
+ * division rounds down. The highest page number we want is the page
+ * where the last byte of the range lives. That last byte has the
+ * address (start + len - 1), thus the highest page number is
+ * (start + len - 1) / page_size. Since we want to include that last
+ * page as well, the loop condition uses <=.
+ */
+ for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+ /* Byte position of the first byte in the range in this page. */
+ /* starthere is an offset to the base address of the chip. */
+ starthere = max(start, i * page_size);
+ /* Length of bytes in the range in this page. */
+ lenhere = min(start + len, (i + 1) * page_size) - starthere;
+ for (j = 0; j < lenhere; j += chunksize) {
+ towrite = min(chunksize, lenhere - j);
+ rc = spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite);
+ if (rc)
+ break;
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10);
+ }
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Program chip using byte programming. (SLOW!)
+ * This is for chips which can only handle one byte writes
+ * and for chips where memory mapped programming is impossible
+ * (e.g. due to size constraints in IT87* for over 512 kB)
+ */
+/* real chunksize is 1, logical chunksize is 1 */
+int spi_chip_write_1(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int i;
+ int result = 0;
+
+ for (i = start; i < start + len; i++) {
+ result = spi_byte_program(flash, i, buf[i - start]);
+ if (result)
+ return 1;
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10);
+ }
+
+ return 0;
+}
+
+int default_spi_write_aai(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ uint32_t pos = start;
+ int result;
+ unsigned char cmd[JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE] = {
+ JEDEC_AAI_WORD_PROGRAM,
+ };
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_AAI_WORD_PROGRAM_OUTSIZE,
+ .writearr = (const unsigned char[]){
+ JEDEC_AAI_WORD_PROGRAM,
+ (start >> 16) & 0xff,
+ (start >> 8) & 0xff,
+ (start & 0xff),
+ buf[0],
+ buf[1]
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ switch (flash->mst->spi.type) {
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+ case SPI_CONTROLLER_IT87XX:
+ case SPI_CONTROLLER_WBSIO:
+ msg_perr("%s: impossible with this SPI controller,"
+ " degrading to byte program\n", __func__);
+ return spi_chip_write_1(flash, buf, start, len);
+#endif
+#endif
+ default:
+ break;
+ }
+
+ /* The even start address and even length requirements can be either
+ * honored outside this function, or we can call spi_byte_program
+ * for the first and/or last byte and use AAI for the rest.
+ * FIXME: Move this to generic code.
+ */
+ /* The data sheet requires a start address with the low bit cleared. */
+ if (start % 2) {
+ msg_cerr("%s: start address not even! Please report a bug at "
+ "flashrom@flashrom.org\n", __func__);
+ if (spi_chip_write_1(flash, buf, start, start % 2))
+ return SPI_GENERIC_ERROR;
+ pos += start % 2;
+ cmds[1].writearr = (const unsigned char[]){
+ JEDEC_AAI_WORD_PROGRAM,
+ (pos >> 16) & 0xff,
+ (pos >> 8) & 0xff,
+ (pos & 0xff),
+ buf[pos - start],
+ buf[pos - start + 1]
+ };
+ /* Do not return an error for now. */
+ //return SPI_GENERIC_ERROR;
+ }
+ /* The data sheet requires total AAI write length to be even. */
+ if (len % 2) {
+ msg_cerr("%s: total write length not even! Please report a "
+ "bug at flashrom@flashrom.org\n", __func__);
+ /* Do not return an error for now. */
+ //return SPI_GENERIC_ERROR;
+ }
+
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result != 0) {
+ msg_cerr("%s failed during start command execution: %d\n", __func__, result);
+ goto bailout;
+ }
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10);
+
+ /* We already wrote 2 bytes in the multicommand step. */
+ pos += 2;
+
+ /* Are there at least two more bytes to write? */
+ while (pos < start + len - 1) {
+ cmd[1] = buf[pos++ - start];
+ cmd[2] = buf[pos++ - start];
+ result = spi_send_command(flash, JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE, 0, cmd, NULL);
+ if (result != 0) {
+ msg_cerr("%s failed during followup AAI command execution: %d\n", __func__, result);
+ goto bailout;
+ }
+ while (spi_read_status_register(flash) & SPI_SR_WIP)
+ programmer_delay(10);
+ }
+
+ /* Use WRDI to exit AAI mode. This needs to be done before issuing any other non-AAI command. */
+ result = spi_write_disable(flash);
+ if (result != 0) {
+ msg_cerr("%s failed to disable AAI mode.\n", __func__);
+ return SPI_GENERIC_ERROR;
+ }
+
+ /* Write remaining byte (if any). */
+ if (pos < start + len) {
+ if (spi_chip_write_1(flash, buf + pos - start, pos, pos % 2))
+ return SPI_GENERIC_ERROR;
+ pos += pos % 2;
+ }
+
+ return 0;
+
+bailout:
+ result = spi_write_disable(flash);
+ if (result != 0)
+ msg_cerr("%s failed to disable AAI mode.\n", __func__);
+ return SPI_GENERIC_ERROR;
+}
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
new file mode 100644
index 0000000..01a6862
--- /dev/null
+++ b/spi25_statusreg.c
@@ -0,0 +1,738 @@
+/*
+ * This file is part of the flashrom project.
+ * It handles everything related to status registers of the JEDEC family 25.
+ *
+ * Copyright (C) 2007, 2008, 2009, 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 coresystems GmbH
+ * Copyright (C) 2008 Ronald Hoogenboom <ronald@zonnet.nl>
+ * Copyright (C) 2012 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+#include "spi.h"
+
+/* === Generic functions === */
+int spi_write_status_enable(struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_EWSR_OUTSIZE] = { JEDEC_EWSR };
+ int result;
+
+ /* Send EWSR (Enable Write Status Register). */
+ result = spi_send_command(flash, sizeof(cmd), JEDEC_EWSR_INSIZE, cmd, NULL);
+
+ if (result)
+ msg_cerr("%s failed\n", __func__);
+
+ return result;
+}
+
+static int spi_write_status_register_flag(struct flashctx *flash, int status, const unsigned char enable_opcode)
+{
+ int result;
+ int i = 0;
+ /*
+ * WRSR requires either EWSR or WREN depending on chip type.
+ * The code below relies on the fact hat EWSR and WREN have the same
+ * INSIZE and OUTSIZE.
+ */
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ enable_opcode },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = JEDEC_WRSR_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution\n", __func__);
+ /* No point in waiting for the command to complete if execution
+ * failed.
+ */
+ return result;
+ }
+ /* WRSR performs a self-timed erase before the changes take effect.
+ * This may take 50-85 ms in most cases, and some chips apparently
+ * allow running RDSR only once. Therefore pick an initial delay of
+ * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+ */
+ programmer_delay(100 * 1000);
+ while (spi_read_status_register(flash) & SPI_SR_WIP) {
+ if (++i > 490) {
+ msg_cerr("Error: WIP bit after WRSR never cleared\n");
+ return TIMEOUT_ERROR;
+ }
+ programmer_delay(10 * 1000);
+ }
+ return 0;
+}
+
+int spi_write_status_register(struct flashctx *flash, int status)
+{
+ int feature_bits = flash->chip->feature_bits;
+ int ret = 1;
+
+ if (!(feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
+ msg_cdbg("Missing status register write definition, assuming "
+ "EWSR is needed\n");
+ feature_bits |= FEATURE_WRSR_EWSR;
+ }
+ if (feature_bits & FEATURE_WRSR_WREN)
+ ret = spi_write_status_register_flag(flash, status, JEDEC_WREN);
+ if (ret && (feature_bits & FEATURE_WRSR_EWSR))
+ ret = spi_write_status_register_flag(flash, status, JEDEC_EWSR);
+ return ret;
+}
+
+uint8_t spi_read_status_register(struct flashctx *flash)
+{
+ static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR };
+ /* FIXME: No workarounds for driver/hardware bugs in generic code. */
+ unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */
+ int ret;
+
+ /* Read Status Register */
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr);
+ if (ret)
+ msg_cerr("RDSR failed!\n");
+
+ return readarr[0];
+}
+
+/* A generic block protection disable.
+ * Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise.
+ * Tests if the register bits are locked with the lock_mask (lock_mask).
+ * Tests if a hardware protection is active (i.e. low pin/high bit value) with the write protection mask
+ * (wp_mask) and bails out in that case.
+ * If there are register lock bits set we try to disable them by unsetting those bits of the previous register
+ * contents that are set in the lock_mask. We then check if removing the lock bits has worked and continue as if
+ * they never had been engaged:
+ * If the lock bits are out of the way try to disable engaged protections.
+ * To support uncommon global unprotects (e.g. on most AT2[56]xx1(A)) unprotect_mask can be used to force
+ * bits to 0 additionally to those set in bp_mask and lock_mask. Only bits set in unprotect_mask are potentially
+ * preserved when doing the final unprotect.
+ *
+ * To sum up:
+ * bp_mask: set those bits that correspond to the bits in the status register that indicate an active protection
+ * (which should be unset after this function returns).
+ * lock_mask: set the bits that correspond to the bits that lock changing the bits above.
+ * wp_mask: set the bits that correspond to bits indicating non-software revocable protections.
+ * unprotect_mask: set the bits that should be preserved if possible when unprotecting.
+ */
+static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_mask, uint8_t lock_mask, uint8_t wp_mask, uint8_t unprotect_mask)
+{
+ uint8_t status;
+ int result;
+
+ status = spi_read_status_register(flash);
+ if ((status & bp_mask) == 0) {
+ msg_cdbg2("Block protection is disabled.\n");
+ return 0;
+ }
+
+ msg_cdbg("Some block protection in effect, disabling... ");
+ if ((status & lock_mask) != 0) {
+ msg_cdbg("\n\tNeed to disable the register lock first... ");
+ if (wp_mask != 0 && (status & wp_mask) == 0) {
+ msg_cerr("Hardware protection is active, disabling write protection is impossible.\n");
+ return 1;
+ }
+ /* All bits except the register lock bit (often called SPRL, SRWD, WPEN) are readonly. */
+ result = spi_write_status_register(flash, status & ~lock_mask);
+ if (result) {
+ msg_cerr("spi_write_status_register failed.\n");
+ return result;
+ }
+ status = spi_read_status_register(flash);
+ if ((status & lock_mask) != 0) {
+ msg_cerr("Unsetting lock bit(s) failed.\n");
+ return 1;
+ }
+ msg_cdbg("done.\n");
+ }
+ /* Global unprotect. Make sure to mask the register lock bit as well. */
+ result = spi_write_status_register(flash, status & ~(bp_mask | lock_mask) & unprotect_mask);
+ if (result) {
+ msg_cerr("spi_write_status_register failed.\n");
+ return result;
+ }
+ status = spi_read_status_register(flash);
+ if ((status & bp_mask) != 0) {
+ msg_cerr("Block protection could not be disabled!\n");
+ flash->chip->printlock(flash);
+ return 1;
+ }
+ msg_cdbg("disabled.\n");
+ return 0;
+}
+
+/* A common block protection disable that tries to unset the status register bits masked by 0x3C. */
+int spi_disable_blockprotect(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x3C, 0, 0, 0xFF);
+}
+
+/* A common block protection disable that tries to unset the status register bits masked by 0x0C (BP0-1) and
+ * protected/locked by bit #7. Useful when bits 4-5 may be non-0). */
+int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF);
+}
+
+/* A common block protection disable that tries to unset the status register bits masked by 0x1C (BP0-2) and
+ * protected/locked by bit #7. Useful when bit #5 is neither a protection bit nor reserved (and hence possibly
+ * non-0). */
+int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0, 0xFF);
+}
+
+/* A common block protection disable that tries to unset the status register bits masked by 0x3C (BP0-3) and
+ * protected/locked by bit #7. */
+int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x3C, 1 << 7, 0, 0xFF);
+}
+
+/* A common block protection disable that tries to unset the status register bits masked by 0x7C (BP0-4) and
+ * protected/locked by bit #7. */
+int spi_disable_blockprotect_bp4_srwd(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF);
+}
+
+static void spi_prettyprint_status_register_hex(uint8_t status)
+{
+ msg_cdbg("Chip status register is 0x%02x.\n", status);
+}
+
+/* Common highest bit: Status Register Write Disable (SRWD) or Status Register Protect (SRP). */
+static void spi_prettyprint_status_register_srwd(uint8_t status)
+{
+ msg_cdbg("Chip status register: Status Register Write Disable (SRWD, SRP, ...) is %sset\n",
+ (status & (1 << 7)) ? "" : "not ");
+}
+
+/* Common highest bit: Block Protect Write Disable (BPL). */
+static void spi_prettyprint_status_register_bpl(uint8_t status)
+{
+ msg_cdbg("Chip status register: Block Protect Write Disable (BPL) is %sset\n",
+ (status & (1 << 7)) ? "" : "not ");
+}
+
+/* Common lowest 2 bits: WEL and WIP. */
+static void spi_prettyprint_status_register_welwip(uint8_t status)
+{
+ msg_cdbg("Chip status register: Write Enable Latch (WEL) is %sset\n",
+ (status & (1 << 1)) ? "" : "not ");
+ msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is %sset\n",
+ (status & (1 << 0)) ? "" : "not ");
+}
+
+/* Common block protection (BP) bits. */
+static void spi_prettyprint_status_register_bp(uint8_t status, int bp)
+{
+ switch (bp) {
+ /* Fall through. */
+ case 4:
+ msg_cdbg("Chip status register: Block Protect 4 (BP4) is %sset\n",
+ (status & (1 << 6)) ? "" : "not ");
+ case 3:
+ msg_cdbg("Chip status register: Block Protect 3 (BP3) is %sset\n",
+ (status & (1 << 5)) ? "" : "not ");
+ case 2:
+ msg_cdbg("Chip status register: Block Protect 2 (BP2) is %sset\n",
+ (status & (1 << 4)) ? "" : "not ");
+ case 1:
+ msg_cdbg("Chip status register: Block Protect 1 (BP1) is %sset\n",
+ (status & (1 << 3)) ? "" : "not ");
+ case 0:
+ msg_cdbg("Chip status register: Block Protect 0 (BP0) is %sset\n",
+ (status & (1 << 2)) ? "" : "not ");
+ }
+}
+
+/* Unnamed bits. */
+void spi_prettyprint_status_register_bit(uint8_t status, int bit)
+{
+ msg_cdbg("Chip status register: Bit %i is %sset\n", bit, (status & (1 << bit)) ? "" : "not ");
+}
+
+int spi_prettyprint_status_register_plain(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+ return 0;
+}
+
+/* Print the plain hex value and the welwip bits only. */
+int spi_prettyprint_status_register_default_welwip(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* Works for many chips of the
+ * AMIC A25L series
+ * and MX MX25L512
+ */
+int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bit(status, 4);
+ spi_prettyprint_status_register_bp(status, 1);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* Works for many chips of the
+ * AMIC A25L series
+ * PMC Pm25LD series
+ */
+int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* Works for many chips of the
+ * ST M25P series
+ * MX MX25L series
+ */
+int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bp(status, 3);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ spi_prettyprint_status_register_bp(status, 4);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_bpl(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_bpl(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ msg_cdbg("Chip status register: Top/Bottom (TB) is %s\n", (status & (1 << 5)) ? "bottom" : "top");
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* === Amic ===
+ * FIXME: spi_disable_blockprotect is incorrect but works fine for chips using
+ * spi_prettyprint_status_register_bp1_srwd or
+ * spi_prettyprint_status_register_bp2_srwd.
+ * FIXME: spi_disable_blockprotect is incorrect and will fail for chips using
+ * spi_prettyprint_status_register_amic_a25l032 if those have locks controlled
+ * by the second status register.
+ */
+
+int spi_prettyprint_status_register_amic_a25l032(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ msg_cdbg("Chip status register: Sector Protect Size (SEC) is %i KB\n", (status & (1 << 6)) ? 4 : 64);
+ msg_cdbg("Chip status register: Top/Bottom (TB) is %s\n", (status & (1 << 5)) ? "bottom" : "top");
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ msg_cdbg("Chip status register 2 is NOT decoded!\n");
+ return 0;
+}
+
+/* === Atmel === */
+
+static void spi_prettyprint_status_register_atmel_at25_wpen(uint8_t status)
+{
+ msg_cdbg("Chip status register: Write Protect Enable (WPEN) is %sset\n",
+ (status & (1 << 7)) ? "" : "not ");
+}
+
+static void spi_prettyprint_status_register_atmel_at25_srpl(uint8_t status)
+{
+ msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) is %sset\n",
+ (status & (1 << 7)) ? "" : "not ");
+}
+
+static void spi_prettyprint_status_register_atmel_at25_epewpp(uint8_t status)
+{
+ msg_cdbg("Chip status register: Erase/Program Error (EPE) is %sset\n",
+ (status & (1 << 5)) ? "" : "not ");
+ msg_cdbg("Chip status register: WP# pin (WPP) is %sasserted\n",
+ (status & (1 << 4)) ? "not " : "");
+}
+
+static void spi_prettyprint_status_register_atmel_at25_swp(uint8_t status)
+{
+ msg_cdbg("Chip status register: Software Protection Status (SWP): ");
+ switch (status & (3 << 2)) {
+ case 0x0 << 2:
+ msg_cdbg("no sectors are protected\n");
+ break;
+ case 0x1 << 2:
+ msg_cdbg("some sectors are protected\n");
+ /* FIXME: Read individual Sector Protection Registers. */
+ break;
+ case 0x3 << 2:
+ msg_cdbg("all sectors are protected\n");
+ break;
+ default:
+ msg_cdbg("reserved for future use\n");
+ break;
+ }
+}
+
+int spi_prettyprint_status_register_at25df(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_srpl(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_atmel_at25_epewpp(status);
+ spi_prettyprint_status_register_atmel_at25_swp(status);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25df_sec(struct flashctx *flash)
+{
+ /* FIXME: We should check the security lockdown. */
+ msg_cdbg("Ignoring security lockdown (if present)\n");
+ msg_cdbg("Ignoring status register byte 2\n");
+ return spi_prettyprint_status_register_at25df(flash);
+}
+
+/* used for AT25F512, AT25F1024(A), AT25F2048 */
+int spi_prettyprint_status_register_at25f(struct flashctx *flash)
+{
+ uint8_t status;
+
+ status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_wpen(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bit(status, 4);
+ spi_prettyprint_status_register_bp(status, 1);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25f512a(struct flashctx *flash)
+{
+ uint8_t status;
+
+ status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_wpen(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bit(status, 4);
+ spi_prettyprint_status_register_bit(status, 3);
+ spi_prettyprint_status_register_bp(status, 0);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25f512b(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_srpl(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_atmel_at25_epewpp(status);
+ spi_prettyprint_status_register_bit(status, 3);
+ spi_prettyprint_status_register_bp(status, 0);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25f4096(struct flashctx *flash)
+{
+ uint8_t status;
+
+ status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_wpen(status);
+ spi_prettyprint_status_register_bit(status, 6);
+ spi_prettyprint_status_register_bit(status, 5);
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25fs010(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_wpen(status);
+ msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
+ "%sset\n", (status & (1 << 6)) ? "" : "not ");
+ msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
+ "%sset\n", (status & (1 << 5)) ? "" : "not ");
+ spi_prettyprint_status_register_bit(status, 4);
+ msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
+ "%sset\n", (status & (1 << 3)) ? "" : "not ");
+ msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
+ "%sset\n", (status & (1 << 2)) ? "" : "not ");
+ /* FIXME: Pretty-print detailed sector protection status. */
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at25fs040(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_wpen(status);
+ spi_prettyprint_status_register_bp(status, 4);
+ /* FIXME: Pretty-print detailed sector protection status. */
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_at26df081a(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_atmel_at25_srpl(status);
+ msg_cdbg("Chip status register: Sequential Program Mode Status (SPM) is %sset\n",
+ (status & (1 << 6)) ? "" : "not ");
+ spi_prettyprint_status_register_atmel_at25_epewpp(status);
+ spi_prettyprint_status_register_atmel_at25_swp(status);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* Some Atmel DataFlash chips support per sector protection bits and the write protection bits in the status
+ * register do indicate if none, some or all sectors are protected. It is possible to globally (un)lock all
+ * sectors at once by writing 0 not only the protection bits (2 and 3) but also completely unrelated bits (4 and
+ * 5) which normally are not touched.
+ * Affected are all known Atmel chips matched by AT2[56]D[FLQ]..1A? but the AT26DF041. */
+int spi_disable_blockprotect_at2x_global_unprotect(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 1 << 4, 0x00);
+}
+
+int spi_disable_blockprotect_at2x_global_unprotect_sec(struct flashctx *flash)
+{
+ /* FIXME: We should check the security lockdown. */
+ msg_cinfo("Ignoring security lockdown (if present)\n");
+ return spi_disable_blockprotect_at2x_global_unprotect(flash);
+}
+
+int spi_disable_blockprotect_at25f(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF);
+}
+
+int spi_disable_blockprotect_at25f512a(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 0, 0xFF);
+}
+
+int spi_disable_blockprotect_at25f512b(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 1 << 4, 0xFF);
+}
+
+int spi_disable_blockprotect_at25fs010(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x6C, 1 << 7, 0, 0xFF);
+ }
+
+int spi_disable_blockprotect_at25fs040(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF);
+}
+
+/* === Eon === */
+
+int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ msg_cdbg("Chip status register: WP# disable (WPDIS) is %sabled\n", (status & (1 << 6)) ? "en " : "dis");
+ spi_prettyprint_status_register_bp(status, 3);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* === Intel/Numonyx/Micron - Spansion === */
+
+int spi_disable_blockprotect_n25q(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_generic(flash, 0x5C, 1 << 7, 0, 0xFF);
+}
+
+int spi_prettyprint_status_register_n25q(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ if (flash->chip->total_size <= 32 / 8 * 1024) /* N25Q16 and N25Q32: reserved */
+ spi_prettyprint_status_register_bit(status, 6);
+ else
+ msg_cdbg("Chip status register: Block Protect 3 (BP3) is %sset\n",
+ (status & (1 << 6)) ? "" : "not ");
+ msg_cdbg("Chip status register: Top/Bottom (TB) is %s\n", (status & (1 << 5)) ? "bottom" : "top");
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* Used by Intel/Numonyx S33 and Spansion S25FL-S chips */
+/* TODO: Clear P_FAIL and E_FAIL with Clear SR Fail Flags Command (30h) here? */
+int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash)
+{
+ return spi_disable_blockprotect_bp2_srwd(flash);
+}
+
+/* Used by Intel/Numonyx S33 and Spansion S25FL-S chips */
+int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_srwd(status);
+ msg_cdbg("Chip status register: Program Fail Flag (P_FAIL) is %sset\n",
+ (status & (1 << 6)) ? "" : "not ");
+ msg_cdbg("Chip status register: Erase Fail Flag (E_FAIL) is %sset\n",
+ (status & (1 << 5)) ? "" : "not ");
+ spi_prettyprint_status_register_bp(status, 2);
+ spi_prettyprint_status_register_welwip(status);
+ return 0;
+}
+
+/* === SST === */
+
+static void spi_prettyprint_status_register_sst25_common(uint8_t status)
+{
+ spi_prettyprint_status_register_hex(status);
+
+ spi_prettyprint_status_register_bpl(status);
+ msg_cdbg("Chip status register: Auto Address Increment Programming (AAI) is %sset\n",
+ (status & (1 << 6)) ? "" : "not ");
+ spi_prettyprint_status_register_bp(status, 3);
+ spi_prettyprint_status_register_welwip(status);
+}
+
+int spi_prettyprint_status_register_sst25(struct flashctx *flash)
+{
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_sst25_common(status);
+ return 0;
+}
+
+int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash)
+{
+ static const char *const bpt[] = {
+ "none",
+ "1F0000H-1FFFFFH",
+ "1E0000H-1FFFFFH",
+ "1C0000H-1FFFFFH",
+ "180000H-1FFFFFH",
+ "100000H-1FFFFFH",
+ "all", "all"
+ };
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_sst25_common(status);
+ msg_cdbg("Resulting block protection : %s\n", bpt[(status & 0x1c) >> 2]);
+ return 0;
+}
+
+int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash)
+{
+ static const char *const bpt[] = {
+ "none",
+ "0x70000-0x7ffff",
+ "0x60000-0x7ffff",
+ "0x40000-0x7ffff",
+ "all blocks", "all blocks", "all blocks", "all blocks"
+ };
+ uint8_t status = spi_read_status_register(flash);
+ spi_prettyprint_status_register_sst25_common(status);
+ msg_cdbg("Resulting block protection : %s\n", bpt[(status & 0x1c) >> 2]);
+ return 0;
+}
diff --git a/sst28sf040.c b/sst28sf040.c
new file mode 100644
index 0000000..4d8cb66
--- /dev/null
+++ b/sst28sf040.c
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2005 coresystems GmbH <stepan@openbios.org>
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+#define AUTO_PG_ERASE1 0x20
+#define AUTO_PG_ERASE2 0xD0
+#define AUTO_PGRM 0x10
+#define CHIP_ERASE 0x30
+#define RESET 0xFF
+#define READ_ID 0x90
+
+int protect_28sf040(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+
+ chip_readb(flash, bios + 0x1823);
+ chip_readb(flash, bios + 0x1820);
+ chip_readb(flash, bios + 0x1822);
+ chip_readb(flash, bios + 0x0418);
+ chip_readb(flash, bios + 0x041B);
+ chip_readb(flash, bios + 0x0419);
+ chip_readb(flash, bios + 0x040A);
+
+ return 0;
+}
+
+int unprotect_28sf040(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+
+ chip_readb(flash, bios + 0x1823);
+ chip_readb(flash, bios + 0x1820);
+ chip_readb(flash, bios + 0x1822);
+ chip_readb(flash, bios + 0x0418);
+ chip_readb(flash, bios + 0x041B);
+ chip_readb(flash, bios + 0x0419);
+ chip_readb(flash, bios + 0x041A);
+
+ return 0;
+}
+
+int erase_sector_28sf040(struct flashctx *flash, unsigned int address,
+ unsigned int sector_size)
+{
+ chipaddr bios = flash->virtual_memory;
+
+ /* This command sequence is very similar to erase_block_82802ab. */
+ chip_writeb(flash, AUTO_PG_ERASE1, bios);
+ chip_writeb(flash, AUTO_PG_ERASE2, bios + address);
+
+ /* wait for Toggle bit ready */
+ toggle_ready_jedec(flash, bios);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+/* chunksize is 1 */
+int write_28sf040(struct flashctx *flash, const uint8_t *src, unsigned int start, unsigned int len)
+{
+ int i;
+ chipaddr bios = flash->virtual_memory;
+ chipaddr dst = flash->virtual_memory + start;
+
+ for (i = 0; i < len; i++) {
+ /* transfer data from source to destination */
+ if (*src == 0xFF) {
+ dst++, src++;
+ /* If the data is 0xFF, don't program it */
+ continue;
+ }
+ /*issue AUTO PROGRAM command */
+ chip_writeb(flash, AUTO_PGRM, dst);
+ chip_writeb(flash, *src++, dst++);
+
+ /* wait for Toggle bit ready */
+ toggle_ready_jedec(flash, bios);
+ }
+
+ return 0;
+}
+
+static int erase_28sf040(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+
+ chip_writeb(flash, CHIP_ERASE, bios);
+ chip_writeb(flash, CHIP_ERASE, bios);
+
+ programmer_delay(10);
+ toggle_ready_jedec(flash, bios);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
+
+int erase_chip_28sf040(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ if ((addr != 0) || (blocklen != flash->chip->total_size * 1024)) {
+ msg_cerr("%s called with incorrect arguments\n",
+ __func__);
+ return -1;
+ }
+ return erase_28sf040(flash);
+}
diff --git a/sst49lfxxxc.c b/sst49lfxxxc.c
new file mode 100644
index 0000000..d6c85c8
--- /dev/null
+++ b/sst49lfxxxc.c
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2005-2007 coresystems GmbH
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+int erase_sector_49lfxxxc(struct flashctx *flash, unsigned int address,
+ unsigned int sector_size)
+{
+ uint8_t status;
+ chipaddr bios = flash->virtual_memory;
+
+ chip_writeb(flash, 0x30, bios);
+ chip_writeb(flash, 0xD0, bios + address);
+
+ status = wait_82802ab(flash);
+ print_status_82802ab(status);
+
+ /* FIXME: Check the status register for errors. */
+ return 0;
+}
diff --git a/sst_fwhub.c b/sst_fwhub.c
new file mode 100644
index 0000000..3dd140e
--- /dev/null
+++ b/sst_fwhub.c
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2009 Kontron Modular Computers
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Adapted from the Intel FW hub stuff for 82802ax parts. */
+
+#include "flash.h"
+
+static int check_sst_fwhub_block_lock(struct flashctx *flash, int offset)
+{
+ chipaddr registers = flash->virtual_registers;
+ uint8_t blockstatus;
+
+ blockstatus = chip_readb(flash, registers + offset + 2);
+ msg_cdbg("Lock status for 0x%06x (size 0x%06x) is %02x, ",
+ offset, flash->chip->page_size, blockstatus);
+ switch (blockstatus & 0x3) {
+ case 0x0:
+ msg_cdbg("full access\n");
+ break;
+ case 0x1:
+ msg_cdbg("write locked\n");
+ break;
+ case 0x2:
+ msg_cdbg("locked open\n");
+ break;
+ case 0x3:
+ msg_cdbg("write locked down\n");
+ break;
+ }
+ /* Return content of the write_locked bit */
+ return blockstatus & 0x1;
+}
+
+static int clear_sst_fwhub_block_lock(struct flashctx *flash, int offset)
+{
+ chipaddr registers = flash->virtual_registers;
+ uint8_t blockstatus;
+
+ blockstatus = check_sst_fwhub_block_lock(flash, offset);
+
+ if (blockstatus) {
+ msg_cdbg("Trying to clear lock for 0x%06x... ", offset);
+ chip_writeb(flash, 0, registers + offset + 2);
+
+ blockstatus = check_sst_fwhub_block_lock(flash, offset);
+ msg_cdbg("%s\n", (blockstatus) ? "failed" : "OK");
+ }
+
+ return blockstatus;
+}
+
+int printlock_sst_fwhub(struct flashctx *flash)
+{
+ int i;
+
+ for (i = 0; i < flash->chip->total_size * 1024; i += flash->chip->page_size)
+ check_sst_fwhub_block_lock(flash, i);
+
+ return 0;
+}
+
+int unlock_sst_fwhub(struct flashctx *flash)
+{
+ int i, ret=0;
+
+ for (i = 0; i < flash->chip->total_size * 1024; i += flash->chip->page_size)
+ {
+ if (clear_sst_fwhub_block_lock(flash, i))
+ {
+ msg_cwarn("Warning: Unlock Failed for block 0x%06x\n", i);
+ ret++;
+ }
+ }
+ return ret;
+}
+
diff --git a/stm50.c b/stm50.c
new file mode 100644
index 0000000..1ae2bc4
--- /dev/null
+++ b/stm50.c
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 Claus Gindhart <claus.gindhart@kontron.com>
+ * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * All ST M50 chips are locked on startup. Most of them have a uniform 64 kB block layout, but some have
+ * a non-uniform block/sector segmentation which has to be handled with more care. Some of the non-uniform
+ * chips support erasing of the 4 kB sectors with another command.
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+static int stm50_erase_sector(struct flashctx *flash, unsigned int addr)
+{
+ chipaddr bios = flash->virtual_memory + addr;
+
+ // clear status register
+ chip_writeb(flash, 0x50, bios);
+ // now start it
+ chip_writeb(flash, 0x32, bios);
+ chip_writeb(flash, 0xd0, bios);
+ programmer_delay(10);
+
+ uint8_t status = wait_82802ab(flash);
+ print_status_82802ab(status);
+
+ return status == 0x80;
+}
+
+/* Some ST M50* chips do support erasing of sectors. This function will derive the erase function to use from
+ * the length of the of the block. For calls that apparently do not address a sector (but a block) we just call
+ * the block erase function instead. FIXME: This duplicates the behavior of the remaining erasers for blocks and
+ * might be fixed when flashrom supports multiple functions per eraser or erasers that do erase parts of the
+ * chip only. */
+int erase_sector_stm50(struct flashctx *flash, unsigned int addr, unsigned int len)
+{
+ if (len == 4096)
+ return stm50_erase_sector(flash, addr);
+ else
+ return erase_block_82802ab(flash, addr, len);
+}
diff --git a/udelay.c b/udelay.c
new file mode 100644
index 0000000..7c6961d
--- /dev/null
+++ b/udelay.c
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LIBPAYLOAD__
+
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "flash.h"
+
+/* loops per microsecond */
+static unsigned long micro = 1;
+
+__attribute__ ((noinline)) void myusec_delay(unsigned int usecs)
+{
+ unsigned long i;
+ for (i = 0; i < usecs * micro; i++) {
+ /* Make sure the compiler doesn't optimize the loop away. */
+ __asm__ volatile ("" : : "rm" (i) );
+ }
+}
+
+static unsigned long measure_os_delay_resolution(void)
+{
+ unsigned long timeusec;
+ struct timeval start, end;
+ unsigned long counter = 0;
+
+ gettimeofday(&start, NULL);
+ timeusec = 0;
+
+ while (!timeusec && (++counter < 1000000000)) {
+ gettimeofday(&end, NULL);
+ timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
+ (end.tv_usec - start.tv_usec);
+ /* Protect against time going forward too much. */
+ if ((end.tv_sec > start.tv_sec) &&
+ ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
+ timeusec = 0;
+ /* Protect against time going backwards during leap seconds. */
+ if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
+ timeusec = 0;
+ }
+ return timeusec;
+}
+
+static unsigned long measure_delay(unsigned int usecs)
+{
+ unsigned long timeusec;
+ struct timeval start, end;
+
+ gettimeofday(&start, NULL);
+ myusec_delay(usecs);
+ gettimeofday(&end, NULL);
+ timeusec = 1000000 * (end.tv_sec - start.tv_sec) +
+ (end.tv_usec - start.tv_usec);
+ /* Protect against time going forward too much. */
+ if ((end.tv_sec > start.tv_sec) &&
+ ((end.tv_sec - start.tv_sec) >= LONG_MAX / 1000000 - 1))
+ timeusec = LONG_MAX;
+ /* Protect against time going backwards during leap seconds. */
+ if ((end.tv_sec < start.tv_sec) || (timeusec > LONG_MAX))
+ timeusec = 1;
+
+ return timeusec;
+}
+
+void myusec_calibrate_delay(void)
+{
+ unsigned long count = 1000;
+ unsigned long timeusec, resolution;
+ int i, tries = 0;
+
+ msg_pinfo("Calibrating delay loop... ");
+ resolution = measure_os_delay_resolution();
+ if (resolution) {
+ msg_pdbg("OS timer resolution is %lu usecs, ", resolution);
+ } else {
+ msg_pinfo("OS timer resolution is unusable. ");
+ }
+
+recalibrate:
+ count = 1000;
+ while (1) {
+ timeusec = measure_delay(count);
+ if (timeusec > 1000000 / 4)
+ break;
+ if (count >= ULONG_MAX / 2) {
+ msg_pinfo("timer loop overflow, reduced precision. ");
+ break;
+ }
+ count *= 2;
+ }
+ tries ++;
+
+ /* Avoid division by zero, but in that case the loop is shot anyway. */
+ if (!timeusec)
+ timeusec = 1;
+
+ /* Compute rounded up number of loops per microsecond. */
+ micro = (count * micro) / timeusec + 1;
+ msg_pdbg("%luM loops per second, ", micro);
+
+ /* Did we try to recalibrate less than 5 times? */
+ if (tries < 5) {
+ /* Recheck our timing to make sure we weren't just hitting
+ * a scheduler delay or something similar.
+ */
+ for (i = 0; i < 4; i++) {
+ if (resolution && (resolution < 10)) {
+ timeusec = measure_delay(100);
+ } else if (resolution &&
+ (resolution < ULONG_MAX / 200)) {
+ timeusec = measure_delay(resolution * 10) *
+ 100 / (resolution * 10);
+ } else {
+ /* This workaround should be active for broken
+ * OS and maybe libpayload. The criterion
+ * here is horrible or non-measurable OS timer
+ * resolution which will result in
+ * measure_delay(100)=0 whereas a longer delay
+ * (1000 ms) may be sufficient
+ * to get a nonzero time measurement.
+ */
+ timeusec = measure_delay(1000000) / 10000;
+ }
+ if (timeusec < 90) {
+ msg_pdbg("delay more than 10%% too short (got "
+ "%lu%% of expected delay), "
+ "recalculating... ", timeusec);
+ goto recalibrate;
+ }
+ }
+ } else {
+ msg_perr("delay loop is unreliable, trying to continue ");
+ }
+
+ /* We're interested in the actual precision. */
+ timeusec = measure_delay(10);
+ msg_pdbg("10 myus = %ld us, ", timeusec);
+ timeusec = measure_delay(100);
+ msg_pdbg("100 myus = %ld us, ", timeusec);
+ timeusec = measure_delay(1000);
+ msg_pdbg("1000 myus = %ld us, ", timeusec);
+ timeusec = measure_delay(10000);
+ msg_pdbg("10000 myus = %ld us, ", timeusec);
+ timeusec = measure_delay(resolution * 4);
+ msg_pdbg("%ld myus = %ld us, ", resolution * 4, timeusec);
+
+ msg_pinfo("OK.\n");
+}
+
+/* Not very precise sleep. */
+void internal_sleep(unsigned int usecs)
+{
+#if IS_WINDOWS
+ Sleep((usecs + 999) / 1000);
+#elif defined(__DJGPP__)
+ sleep(usecs / 1000000);
+ usleep(usecs % 1000000);
+#else
+ nanosleep(&(struct timespec){usecs / 1000000, (usecs * 1000) % 1000000000UL}, NULL);
+#endif
+}
+
+/* Precise delay. */
+void internal_delay(unsigned int usecs)
+{
+ /* If the delay is >1 s, use internal_sleep because timing does not need to be so precise. */
+ if (usecs > 1000000) {
+ internal_sleep(usecs);
+ } else {
+ myusec_delay(usecs);
+ }
+}
+
+#else
+#include <libpayload.h>
+
+void myusec_calibrate_delay(void)
+{
+ get_cpu_speed();
+}
+
+void internal_delay(unsigned int usecs)
+{
+ udelay(usecs);
+}
+#endif
diff --git a/usbblaster_spi.c b/usbblaster_spi.c
new file mode 100644
index 0000000..7a609f1
--- /dev/null
+++ b/usbblaster_spi.c
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2012 James Laird <jhl@mafipulation.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Device should be connected as per "active serial" mode:
+ *
+ * +---------+------+-----------+
+ * | SPI | Pin | Altera |
+ * +---------+------+-----------+
+ * | SCLK | 1 | DCLK |
+ * | GND | 2,10 | GND |
+ * | VCC | 4 | VCC(TRGT) |
+ * | MISO | 7 | DATAOUT |
+ * | /CS | 8 | nCS |
+ * | MOSI | 9 | ASDI |
+ * +---------+------+-----------+
+ *
+ * See also the USB-Blaster Download Cable User Guide: http://www.altera.com/literature/ug/ug_usb_blstr.pdf
+ */
+
+#if CONFIG_USBBLASTER_SPI == 1
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <ftdi.h>
+#include "flash.h"
+#include "programmer.h"
+#include "spi.h"
+
+/* Please keep sorted by vendor ID, then device ID. */
+#define ALTERA_VID 0x09fb
+#define ALTERA_USBBLASTER_PID 0x6001
+
+const struct dev_entry devs_usbblasterspi[] = {
+ {ALTERA_VID, ALTERA_USBBLASTER_PID, OK, "Altera", "USB-Blaster"},
+
+ {}
+};
+
+static const struct spi_master spi_master_usbblaster;
+
+static struct ftdi_context ftdic;
+
+// command bytes
+#define BIT_BYTE (1<<7) // byte mode (rather than bitbang)
+#define BIT_READ (1<<6) // read request
+#define BIT_LED (1<<5)
+#define BIT_CS (1<<3)
+#define BIT_TMS (1<<1)
+#define BIT_CLK (1<<0)
+
+#define BUF_SIZE 64
+
+/* The programmer shifts bits in the wrong order for SPI, so we use this method to reverse the bits when needed.
+ * http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */
+uint8_t reverse(uint8_t b)
+{
+ return ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
+}
+
+
+/* Returns 0 upon success, a negative number upon errors. */
+int usbblaster_spi_init(void)
+{
+ uint8_t buf[BUF_SIZE + 1];
+
+ if (ftdi_init(&ftdic) < 0)
+ return -1;
+
+ if (ftdi_usb_open(&ftdic, ALTERA_VID, ALTERA_USBBLASTER_PID) < 0) {
+ msg_perr("Failed to open USB-Blaster: %s\n", ftdic.error_str);
+ return -1;
+ }
+
+ if (ftdi_usb_reset(&ftdic) < 0) {
+ msg_perr("USB-Blaster reset failed\n");
+ return -1;
+ }
+
+ if (ftdi_set_latency_timer(&ftdic, 2) < 0) {
+ msg_perr("USB-Blaster set latency timer failed\n");
+ return -1;
+ }
+
+ if (ftdi_write_data_set_chunksize(&ftdic, 4096) < 0 ||
+ ftdi_read_data_set_chunksize(&ftdic, BUF_SIZE) < 0) {
+ msg_perr("USB-Blaster set chunk size failed\n");
+ return -1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf[sizeof(buf)-1] = BIT_LED | BIT_CS;
+ if (ftdi_write_data(&ftdic, buf, sizeof(buf)) < 0) {
+ msg_perr("USB-Blaster reset write failed\n");
+ return -1;
+ }
+ if (ftdi_read_data(&ftdic, buf, sizeof(buf)) < 0) {
+ msg_perr("USB-Blaster reset read failed\n");
+ return -1;
+ }
+
+ register_spi_master(&spi_master_usbblaster);
+ return 0;
+}
+
+static int send_write(unsigned int writecnt, const unsigned char *writearr)
+{
+ int i;
+ uint8_t buf[BUF_SIZE];
+
+ memset(buf, 0, sizeof(buf));
+ while (writecnt) {
+ unsigned int n_write = min(writecnt, BUF_SIZE - 1);
+ msg_pspew("writing %d-byte packet\n", n_write);
+
+ buf[0] = BIT_BYTE | (uint8_t)n_write;
+ for (i = 0; i < n_write; i++) {
+ buf[i+1] = reverse(writearr[i]);
+ }
+ if (ftdi_write_data(&ftdic, buf, n_write + 1) < 0) {
+ msg_perr("USB-Blaster write failed\n");
+ return -1;
+ }
+
+ writearr += n_write;
+ writecnt -= n_write;
+ }
+ return 0;
+}
+
+static int send_read(unsigned int readcnt, unsigned char *readarr)
+{
+ int i;
+ unsigned int n_read;
+ uint8_t buf[BUF_SIZE];
+ memset(buf, 0, sizeof(buf));
+
+ n_read = readcnt;
+ while (n_read) {
+ unsigned int payload_size = min(n_read, BUF_SIZE - 1);
+ msg_pspew("reading %d-byte packet\n", payload_size);
+
+ buf[0] = BIT_BYTE | BIT_READ | (uint8_t)payload_size;
+ if (ftdi_write_data(&ftdic, buf, payload_size + 1) < 0) {
+ msg_perr("USB-Blaster write failed\n");
+ return -1;
+ }
+ n_read -= payload_size;
+ };
+
+ n_read = readcnt;
+ while (n_read) {
+ int ret = ftdi_read_data(&ftdic, readarr, n_read);
+ if (ret < 0) {
+ msg_perr("USB-Blaster read failed\n");
+ return -1;
+ }
+ for (i = 0; i < ret; i++) {
+ readarr[i] = reverse(readarr[i]);
+ }
+ n_read -= ret;
+ readarr += ret;
+ }
+ return 0;
+}
+
+/* Returns 0 upon success, a negative number upon errors. */
+static int usbblaster_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+ const unsigned char *writearr, unsigned char *readarr)
+{
+ uint8_t cmd;
+ int ret = 0;
+
+ cmd = BIT_LED; // asserts /CS
+ if (ftdi_write_data(&ftdic, &cmd, 1) < 0) {
+ msg_perr("USB-Blaster enable chip select failed\n");
+ ret = -1;
+ }
+
+ if (!ret && writecnt)
+ ret = send_write(writecnt, writearr);
+
+ if (!ret && readcnt)
+ ret = send_read(readcnt, readarr);
+
+ cmd = BIT_CS;
+ if (ftdi_write_data(&ftdic, &cmd, 1) < 0) {
+ msg_perr("USB-Blaster disable chip select failed\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+static const struct spi_master spi_master_usbblaster = {
+ .type = SPI_CONTROLLER_USBBLASTER,
+ .max_data_read = 256,
+ .max_data_write = 256,
+ .command = usbblaster_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = default_spi_read,
+ .write_256 = default_spi_write_256,
+ .write_aai = default_spi_write_aai,
+};
+
+#endif
diff --git a/w29ee011.c b/w29ee011.c
new file mode 100644
index 0000000..4df4687
--- /dev/null
+++ b/w29ee011.c
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007 Markus Boas <ryven@ryven.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "flash.h"
+
+/* According to the Winbond W29EE011, W29EE012, W29C010M, W29C011A
+ * datasheets this is the only valid probe function for those chips.
+ */
+int probe_w29ee011(struct flashctx *flash)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t id1, id2;
+
+ if (!chip_to_probe || strcmp(chip_to_probe, flash->chip->name)) {
+ msg_cdbg("Old Winbond W29* probe method disabled because "
+ "the probing sequence puts the AMIC A49LF040A in "
+ "a funky state. Use 'flashrom -c %s' if you "
+ "have a board with such a chip.\n", flash->chip->name);
+ return 0;
+ }
+
+ /* Issue JEDEC Product ID Entry command */
+ chip_writeb(flash, 0xAA, bios + 0x5555);
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + 0x2AAA);
+ programmer_delay(10);
+ chip_writeb(flash, 0x80, bios + 0x5555);
+ programmer_delay(10);
+ chip_writeb(flash, 0xAA, bios + 0x5555);
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + 0x2AAA);
+ programmer_delay(10);
+ chip_writeb(flash, 0x60, bios + 0x5555);
+ programmer_delay(10);
+
+ /* Read product ID */
+ id1 = chip_readb(flash, bios);
+ id2 = chip_readb(flash, bios + 0x01);
+
+ /* Issue JEDEC Product ID Exit command */
+ chip_writeb(flash, 0xAA, bios + 0x5555);
+ programmer_delay(10);
+ chip_writeb(flash, 0x55, bios + 0x2AAA);
+ programmer_delay(10);
+ chip_writeb(flash, 0xF0, bios + 0x5555);
+ programmer_delay(10);
+
+ msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+ if (id1 == flash->chip->manufacture_id && id2 == flash->chip->model_id)
+ return 1;
+
+ return 0;
+}
diff --git a/w39.c b/w39.c
new file mode 100644
index 0000000..4dd366c
--- /dev/null
+++ b/w39.c
@@ -0,0 +1,243 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 coresystems GmbH
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+
+static uint8_t w39_idmode_readb(struct flashctx *flash, unsigned int offset)
+{
+ chipaddr bios = flash->virtual_memory;
+ uint8_t val;
+
+ /* Product Identification Entry */
+ chip_writeb(flash, 0xAA, bios + 0x5555);
+ chip_writeb(flash, 0x55, bios + 0x2AAA);
+ chip_writeb(flash, 0x90, bios + 0x5555);
+ programmer_delay(10);
+
+ /* Read something, maybe hardware lock bits */
+ val = chip_readb(flash, bios + offset);
+
+ /* Product Identification Exit */
+ chip_writeb(flash, 0xAA, bios + 0x5555);
+ chip_writeb(flash, 0x55, bios + 0x2AAA);
+ chip_writeb(flash, 0xF0, bios + 0x5555);
+ programmer_delay(10);
+
+ return val;
+}
+
+static int printlock_w39_tblwp(uint8_t lock)
+{
+ msg_cdbg("Hardware bootblock locking (#TBL) is %sactive.\n",
+ (lock & (1 << 2)) ? "" : "not ");
+ msg_cdbg("Hardware remaining chip locking (#WP) is %sactive..\n",
+ (lock & (1 << 3)) ? "" : "not ");
+ if (lock & ((1 << 2) | (1 << 3)))
+ return -1;
+
+ return 0;
+}
+
+static int printlock_w39_single_bootblock(uint8_t lock, uint16_t kB)
+{
+ msg_cdbg("Software %d kB bootblock locking is %sactive.\n", kB, (lock & 0x03) ? "" : "not ");
+ if (lock & 0x03)
+ return -1;
+
+ return 0;
+}
+
+static int printlock_w39_bootblock_64k16k(uint8_t lock)
+{
+ msg_cdbg("Software 64 kB bootblock locking is %sactive.\n",
+ (lock & (1 << 0)) ? "" : "not ");
+ msg_cdbg("Software 16 kB bootblock locking is %sactive.\n",
+ (lock & (1 << 1)) ? "" : "not ");
+ if (lock & ((1 << 1) | (1 << 0)))
+ return -1;
+
+ return 0;
+}
+
+static int printlock_w39_common(struct flashctx *flash, unsigned int offset)
+{
+ uint8_t lock;
+
+ lock = w39_idmode_readb(flash, offset);
+ msg_cdbg("Lockout bits:\n");
+ return printlock_w39_tblwp(lock);
+}
+
+int printlock_w39f010(struct flashctx *flash)
+{
+ uint8_t lock;
+ int ret;
+
+ lock = w39_idmode_readb(flash, 0x00002);
+ msg_cdbg("Bottom boot block:\n");
+ ret = printlock_w39_single_bootblock(lock, 16);
+
+ lock = w39_idmode_readb(flash, 0x1fff2);
+ msg_cdbg("Top boot block:\n");
+ ret |= printlock_w39_single_bootblock(lock, 16);
+
+ return ret;
+}
+
+int printlock_w39l010(struct flashctx *flash)
+{
+ uint8_t lock;
+ int ret;
+
+ lock = w39_idmode_readb(flash, 0x00002);
+ msg_cdbg("Bottom boot block:\n");
+ ret = printlock_w39_single_bootblock(lock, 8);
+
+ lock = w39_idmode_readb(flash, 0x1fff2);
+ msg_cdbg("Top boot block:\n");
+ ret |= printlock_w39_single_bootblock(lock, 8);
+
+ return ret;
+}
+
+int printlock_w39l020(struct flashctx *flash)
+{
+ uint8_t lock;
+ int ret;
+
+ lock = w39_idmode_readb(flash, 0x00002);
+ msg_cdbg("Bottom boot block:\n");
+ ret = printlock_w39_bootblock_64k16k(lock);
+
+ lock = w39_idmode_readb(flash, 0x3fff2);
+ msg_cdbg("Top boot block:\n");
+ ret |= printlock_w39_bootblock_64k16k(lock);
+
+ return ret;
+}
+
+int printlock_w39l040(struct flashctx *flash)
+{
+ uint8_t lock;
+ int ret;
+
+ lock = w39_idmode_readb(flash, 0x00002);
+ msg_cdbg("Bottom boot block:\n");
+ ret = printlock_w39_bootblock_64k16k(lock);
+
+ lock = w39_idmode_readb(flash, 0x7fff2);
+ msg_cdbg("Top boot block:\n");
+ ret |= printlock_w39_bootblock_64k16k(lock);
+
+ return ret;
+}
+
+int printlock_w39v040a(struct flashctx *flash)
+{
+ uint8_t lock;
+ int ret = 0;
+
+ /* The W39V040A datasheet contradicts itself on the lock register
+ * location: 0x00002 and 0x7fff2 are both mentioned. Pick the one
+ * which is similar to the other chips of the same family.
+ */
+ lock = w39_idmode_readb(flash, 0x7fff2);
+ msg_cdbg("Lockout bits:\n");
+
+ ret = printlock_w39_tblwp(lock);
+ ret |= printlock_w39_bootblock_64k16k(lock);
+
+ return ret;
+}
+
+int printlock_w39v040b(struct flashctx *flash)
+{
+ return printlock_w39_common(flash, 0x7fff2);
+}
+
+int printlock_w39v040c(struct flashctx *flash)
+{
+ /* Typo in the datasheet? The other chips use 0x7fff2. */
+ return printlock_w39_common(flash, 0xfff2);
+}
+
+int printlock_w39v040fa(struct flashctx *flash)
+{
+ int ret = 0;
+
+ ret = printlock_w39v040a(flash);
+ ret |= printlock_regspace2_uniform_64k(flash);
+
+ return ret;
+}
+
+int printlock_w39v040fb(struct flashctx *flash)
+{
+ int ret = 0;
+
+ ret = printlock_w39v040b(flash);
+ ret |= printlock_regspace2_uniform_64k(flash);
+
+ return ret;
+}
+
+int printlock_w39v040fc(struct flashctx *flash)
+{
+ int ret = 0;
+
+ /* W39V040C and W39V040FC use different WP/TBL offsets. */
+ ret = printlock_w39_common(flash, 0x7fff2);
+ ret |= printlock_regspace2_uniform_64k(flash);
+
+ return ret;
+}
+
+int printlock_w39v080a(struct flashctx *flash)
+{
+ return printlock_w39_common(flash, 0xffff2);
+}
+
+int printlock_w39v080fa(struct flashctx *flash)
+{
+ int ret = 0;
+
+ ret = printlock_w39v080a(flash);
+ ret |= printlock_regspace2_uniform_64k(flash);
+
+ return ret;
+}
+
+int printlock_w39v080fa_dual(struct flashctx *flash)
+{
+ msg_cinfo("Block locking for W39V080FA in dual mode is "
+ "undocumented.\n");
+ /* Better safe than sorry. */
+ return -1;
+}
+
+int printlock_at49f(struct flashctx *flash)
+{
+ uint8_t lock = w39_idmode_readb(flash, 0x00002);
+ msg_cdbg("Hardware bootblock lockout is %sactive.\n",
+ (lock & 0x01) ? "" : "not ");
+ return 0;
+}
diff --git a/wbsio_spi.c b/wbsio_spi.c
new file mode 100644
index 0000000..0eeeb9b
--- /dev/null
+++ b/wbsio_spi.c
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 Peter Stuge <peter@stuge.se>
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "flash.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "hwaccess.h"
+#include "spi.h"
+
+#define WBSIO_PORT1 0x2e
+#define WBSIO_PORT2 0x4e
+
+static uint16_t wbsio_spibase = 0;
+
+static uint16_t wbsio_get_spibase(uint16_t port)
+{
+ uint8_t id;
+ uint16_t flashport = 0;
+
+ w836xx_ext_enter(port);
+ id = sio_read(port, 0x20);
+ if (id != 0xa0) {
+ msg_perr("\nW83627 not found at 0x%x, id=0x%02x want=0xa0.\n", port, id);
+ goto done;
+ }
+
+ if (0 == (sio_read(port, 0x24) & 2)) {
+ msg_perr("\nW83627 found at 0x%x, but SPI pins are not enabled. (CR[0x24] bit 1=0)\n", port);
+ goto done;
+ }
+
+ sio_write(port, 0x07, 0x06);
+ if (0 == (sio_read(port, 0x30) & 1)) {
+ msg_perr("\nW83627 found at 0x%x, but SPI is not enabled. (LDN6[0x30] bit 0=0)\n", port);
+ goto done;
+ }
+
+ flashport = (sio_read(port, 0x62) << 8) | sio_read(port, 0x63);
+
+done:
+ w836xx_ext_leave(port);
+ return flashport;
+}
+
+static int wbsio_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr);
+static int wbsio_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len);
+
+static const struct spi_master spi_master_wbsio = {
+ .type = SPI_CONTROLLER_WBSIO,
+ .max_data_read = MAX_DATA_UNSPECIFIED,
+ .max_data_write = MAX_DATA_UNSPECIFIED,
+ .command = wbsio_spi_send_command,
+ .multicommand = default_spi_send_multicommand,
+ .read = wbsio_spi_read,
+ .write_256 = spi_chip_write_1,
+ .write_aai = default_spi_write_aai,
+};
+
+int wbsio_check_for_spi(void)
+{
+ if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT1)))
+ if (0 == (wbsio_spibase = wbsio_get_spibase(WBSIO_PORT2)))
+ return 1;
+
+ msg_pspew("\nwbsio_spibase = 0x%x\n", wbsio_spibase);
+
+ msg_pdbg("%s: Winbond saved on 4 register bits so max chip size is "
+ "1024 kB!\n", __func__);
+ max_rom_decode.spi = 1024 * 1024;
+ register_spi_master(&spi_master_wbsio);
+
+ return 0;
+}
+
+/* W83627DHG has 11 command modes:
+ * 1=1 command only
+ * 2=1 command+1 data write
+ * 3=1 command+2 data read
+ * 4=1 command+3 address
+ * 5=1 command+3 address+1 data write
+ * 6=1 command+3 address+4 data write
+ * 7=1 command+3 address+1 dummy address inserted by wbsio+4 data read
+ * 8=1 command+3 address+1 data read
+ * 9=1 command+3 address+2 data read
+ * a=1 command+3 address+3 data read
+ * b=1 command+3 address+4 data read
+ *
+ * mode[7:4] holds the command mode
+ * mode[3:0] holds SPI address bits [19:16]
+ *
+ * The Winbond SPI master only supports 20 bit addresses on the SPI bus. :\
+ * Would one more byte of RAM in the chip (to get all 24 bits) really make
+ * such a big difference?
+ */
+static int wbsio_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+ unsigned int readcnt,
+ const unsigned char *writearr,
+ unsigned char *readarr)
+{
+ int i;
+ uint8_t mode = 0;
+
+ msg_pspew("%s:", __func__);
+
+ if (1 == writecnt && 0 == readcnt) {
+ mode = 0x10;
+ } else if (2 == writecnt && 0 == readcnt) {
+ OUTB(writearr[1], wbsio_spibase + 4);
+ msg_pspew(" data=0x%02x", writearr[1]);
+ mode = 0x20;
+ } else if (1 == writecnt && 2 == readcnt) {
+ mode = 0x30;
+ } else if (4 == writecnt && 0 == readcnt) {
+ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f));
+ for (i = 2; i < writecnt; i++) {
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew("%02x", writearr[i]);
+ }
+ mode = 0x40 | (writearr[1] & 0x0f);
+ } else if (5 == writecnt && 0 == readcnt) {
+ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f));
+ for (i = 2; i < 4; i++) {
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew("%02x", writearr[i]);
+ }
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew(" data=0x%02x", writearr[i]);
+ mode = 0x50 | (writearr[1] & 0x0f);
+ } else if (8 == writecnt && 0 == readcnt) {
+ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f));
+ for (i = 2; i < 4; i++) {
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew("%02x", writearr[i]);
+ }
+ msg_pspew(" data=0x");
+ for (; i < writecnt; i++) {
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew("%02x", writearr[i]);
+ }
+ mode = 0x60 | (writearr[1] & 0x0f);
+ } else if (5 == writecnt && 4 == readcnt) {
+ /* XXX: TODO not supported by flashrom infrastructure!
+ * This mode, 7, discards the fifth byte in writecnt,
+ * but since we can not express that in flashrom, fail
+ * the operation for now.
+ */
+ ;
+ } else if (4 == writecnt && readcnt >= 1 && readcnt <= 4) {
+ msg_pspew(" addr=0x%02x", (writearr[1] & 0x0f));
+ for (i = 2; i < writecnt; i++) {
+ OUTB(writearr[i], wbsio_spibase + i);
+ msg_pspew("%02x", writearr[i]);
+ }
+ mode = ((7 + readcnt) << 4) | (writearr[1] & 0x0f);
+ }
+ msg_pspew(" cmd=%02x mode=%02x\n", writearr[0], mode);
+
+ if (!mode) {
+ msg_perr("%s: unsupported command type wr=%d rd=%d\n",
+ __func__, writecnt, readcnt);
+ /* Command type refers to the number of bytes read/written. */
+ return SPI_INVALID_LENGTH;
+ }
+
+ OUTB(writearr[0], wbsio_spibase);
+ OUTB(mode, wbsio_spibase + 1);
+ programmer_delay(10);
+
+ if (!readcnt)
+ return 0;
+
+ msg_pspew("%s: returning data =", __func__);
+ for (i = 0; i < readcnt; i++) {
+ readarr[i] = INB(wbsio_spibase + 4 + i);
+ msg_pspew(" 0x%02x", readarr[i]);
+ }
+ msg_pspew("\n");
+ return 0;
+}
+
+static int wbsio_spi_read(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ mmio_readn((void *)(flash->virtual_memory + start), buf, len);
+ return 0;
+}
+
+#endif
OpenPOWER on IntegriCloud