OpenWrt Forum Archive

Topic: U-Boot mod for routers with AR9331/AR9344

The content of this topic has been archived between 3 Apr 2015 and 7 May 2018. Unfortunately there are posts – most likely complete pages – missing.

Hello,

Recently I've made an upgrade my TL-MR3020 from 4 MB to 16M flash.
I wonder, why not to go to 32M ?

Looking through Forum - there's a common answer that chips doesn't exist.
But this is not true.
When I was uprgading to 16M I was using Winbond W25Q128FV. This is not SOIC 208mil package, but WSON 8x6mm.
This is nearly the same. Preferably it should be deployed on the PCB with Hot Air soldering station. But you can solder it with the ordinary Iron too... Same dimentions. Same pins. One-to-one replacement possible.

Here is the link to Winbond W25Q256FV chip. 32Mb. WSON 8x6mm package. Same 8 pads.
http://www.winbond.com/NR/rdonlyres/658 … Q256FV.pdf

Have anyone tried this?
Yes, It is definitely hard to buy these chips.

Elektronik wrote:

Recently I've made an upgrade my TL-MR3020 from 4 MB to 16M flash.
I wonder, why not to go to 32M ?

32MB Flash MR13U (AR9331) -> https://forum.openwrt.org/viewtopic.php?id=54505

"However my MR13U comes with a custom uboot where additional changes are made to support 32MB-Flash."

Seems to be doable, but with quite some effort.

May be I'm really a noob, but this looks like not so hard to do...
I looked into the Kernel Source and some 256Mbit chips are already known by the Kernel. Spansion - no, Winbond - Yes.
I'd guess this means that there's no need to re-build the firmware for 32M, and only modify the U-boot to support 32 Mb and Map partitions.

Possibly I'm wrong.

I'm game for this.  Does anyone know where to buy a W25Q256 chip?  They are "non stock" at Digi-Key.

The 32 MB chips use a different SPI command access the top 16 MB.  This is because the standard SPI commands for 16MB and smaller chips only use three address bytes, which is only sufficient to address a 16 MB space (0x1000000 = 16Mi).  The W25Q256 includes a compatibility mode where the first 16 MB can be read/written using the old commands, and a page selecting scheme where 16 MB blocks of the chip can be swapped into the 24-bit address space.  Probably the kernel is using page selection, since the hardware SPI flash controller is likely not aware of the 32-bit addressing mode.

But all that you really need the bootloader to do is access the first 4MB, which the stock one should do in compatibilty mode.  As long as your kernel is in that space, it should be able to boot it, then the kernel will take over and be able to access the whole chip.  To get the ART in place, and/or to flash an image that is larger than 4 MB, you can load and boot a RAM-based OpenWrt which has ART writeable.

mk24 wrote:

I'm game for this.  Does anyone know where to buy a W25Q256 chip?  They are "non stock" at Digi-Key.

It depends what country are you from.
I.e. In Russia it is possible to search ICs via http:\\efind.ru - complete search of availability on local and international stocks.

There I see that in Canada You can buy here: http://abtronics.ca/search/#name=w25q256

Hello,
What SPI programmer and software are you all using to flash the EEPROM chips? I am new at doing this, but would like to learn. I can get this programmer (CH341A based) and it says it supports the W25Q64 chip (and also the W25Q128?). Does that work? Looks similar to a picture I saw in another thread.

Also, what software is used to flash the chip? All I know is AVRDUDE from arduino. I don't think that will work. Is there another? I see Ponyprog. Is that what people use?

Thank you very much!

jamdat wrote:

Hello,
What SPI programmer and software are you all using to flash the EEPROM chips? I am new at doing this, but would like to learn. I can get this programmer (CH341A based)

Hi.
I was using CH341A Clone. All of them are the same and work with the same software. Used a clone, because it was easy to buy it locally (and FAST).

It is generic SPI flasher hardware. THis means - it will support ANY flash chip. The question is in software only.
And here you come to some options:
1. Buy CH341A clone and google CH341 Programmer Software. You'd probably get version 1.18. It works. Maximum supported chips are 16MiB.
2. Buy original CH341A and get the latest software on the CD with the programmer. Register it and use. latest version 1.27 and it supports 32MiB too.

Anyhow you are going to buy at AliExpress - Think of buying original one. The price is the same. May be more problems with ordering (Chineese lang).
Here is the link to official page of the CH341 Programmer.
First 4 links is the links to the official SHOPS.

http://blog.163.com/skygz@126/blog/stat … 742153122/

Hi pepe2k,

I've tried to use RAM version of u-boot for router debrick,
but there is very strange situation.

RAM version can't start stable for each launch.
Let me describe in details:

I use mr3020 v1.8 router with ar9331 on board.
I use j-link in conjunction with openocd 0.8.0.
I use pll and RAM init sequence from here:
https://wikidevi.com/wiki/TP-LINK_TL-MR3020#JTAG
Commands for openocd are:

halt
reset init
load_image uboot_for_tp-link_tl-mr3020__RAM.bin 0x80100000
resume 0x80100000

Image loaded successfully. I can verify it.
I can see RAM version logo for each start,
but usually it hangs at relocate_code function
near code

1:
        lw      t3, 0(t0)
        sw      t3, 0(t1)
        addu    t0, 4
        ble     t0, t2, 1b


I can halt processor,but RAM is not accessible anymore.
I've tried different version of u-boot from git,
but behaviour is the same.

It can start once for 20...30 attempts approximately.

JTAG frequency change doesn't help.
CPU/AHB frequency change doesn't help.
Router works well if I desolder flash and programm it.
But I would like to use RAM version of u-boot.

Where is my mistake?
Do you have any idea?

P.S. I'm sorry. My English is not good enough.

jamdat wrote:

Hello,
What SPI programmer and software are you all using to flash the EEPROM chips? I am new at doing this, but would like to learn. I can get this programmer (CH341A based) and it says it supports the W25Q64 chip (and also the W25Q128?). Does that work? Looks similar to a picture I saw in another thread.

Also, what software is used to flash the chip? All I know is AVRDUDE from arduino. I don't think that will work. Is there another? I see Ponyprog. Is that what people use?

Thank you very much!


I'm using "flashrom" with TIAO USB Multi-Protocol Adapter (http://www.diygadget.com/tiao-usb-multi … erial.html) , and one of these clips: http://www.ebay.com/itm/141377097279?_t … EBIDX%3AIT. It seems there are a bunch of different versions of flashrom floating around so I recommend getting the sources directly from their site and compiling yourself. The W25Q256 is supported in the latest version. I have a bunch of other much more fancy programmers (and even designed one some years ago) but for this application the combination above works well. I've also found that I've been able to program most boards using serial flash in-circuit with the TIAO board (hence the clip I gave the link to).

dony71 wrote:

[...]
hi pepe2k,
long forgotten bugs
have you got chance to take a look on this?

ander wrote:

Hi pepe2k,

I've tried to use RAM version of u-boot for router debrick,
but there is very strange situation.
[...]

Please, use: https://github.com/pepe2k/u-boot_mod/issues/new
I don't follow this forum.

Hello all,
at first, sorry for my stupid question :-)
I would like to change flash memory on my router WR740N (I successfuly changed ram to 64M. :-)).
I bought Spansion S25FL127S, but I have problem with programming.
I wanted do it in the way according https://dev.wlan-si.net/wiki/Routers/TP … lashMemory
As image I used uboot_for_tp-link_tl-wr740n_v4.bin from pepe2k (thank you). But I have probably wrong flash layout. Could somebody give me the right one? Or any other way, how to install it to the new clear Flash memory?
Thank you in advance

[solved]
At the end, I copy the flash by the method with switching FLASHs during run state. Described here: http://dwhacks.blogspot.de/2013/03/been-while.html
I cannot used both flash in one time (and just switch ChipSelect pin). I had to switch whole flash memory.
And it works well.
After flash I had a problem with ART - described way doesn't work for my (or I made something in wrong way). For ART flash I used in-build web mode  https://forum.openwrt.org/viewtopic.php?id=43237 (reset for 3 seconds) http://192.168.1.1/art.html
And it works!! Now I can finally solder flash to the router :-)

(Last edited by jirka.nachod on 29 Jun 2015, 20:39)

@pepe2k: thanks a lot for this project, it looks like it will make my router unbrikkable!


@all: One quick question
I'm hoing to install this on my tl-mr3220 v2 router shortly, and i'd like to install it with the openwrt method. As per official pepe2k wiki ( https://github.com/pepe2k/u-boot_mod#using-openwrt ) I should:
* download openwrt sources and compile a special version of openwrt with unlocked U-Boot partition
* download u-boot_mod sources and compile the modified boot image
However, I found a website where both the special openwrt and the u-boot_mod bin images are provided. Anybody tried them? Are they safe to flash or it is better to compile my own? The website is http://ofmodemsandmen.com/downloadso.html#other

clemmy wrote:

@pepe2k: thanks a lot for this project, it looks like it will make my router unbrikkable!


@all: One quick question
I'm hoing to install this on my tl-mr3220 v2 router shortly, and i'd like to install it with the openwrt method. As per official pepe2k wiki ( https://github.com/pepe2k/u-boot_mod#using-openwrt ) I should:
* download openwrt sources and compile a special version of openwrt with unlocked U-Boot partition
* download u-boot_mod sources and compile the modified boot image
However, I found a website where both the special openwrt and the u-boot_mod bin images are provided. Anybody tried them? Are they safe to flash or it is better to compile my own? The website is http://ofmodemsandmen.com/downloadso.html#other

You can use my official release: https://github.com/pepe2k/u-boot_mod/re … 2014-11-19

There is everything you need (read README), including OpenWrt BB version with unlocked uboot MTD partition, embedded dedicated U-Boot image and dedicated script for updating.

pepe2k wrote:

You can use my official release: https://github.com/pepe2k/u-boot_mod/re … 2014-11-19

There is everything you need (read README), including OpenWrt BB version with unlocked uboot MTD partition, embedded dedicated U-Boot image and dedicated script for updating.

Oh... Thanks a lot!! smile

I did look through your github for quite some time, assuming that there should be bin images somewhere, but was unable to find them! Turns out I was just browsing source code, and was not aware of the "releases" section lol

Nice work!!

Hi,

I successfully flashed u-boot from the master release ( https://github.com/pepe2k/u-boot_mod/re … 2014-11-19 ) to my WDR-3600.

Now I want to test some overclocking settings, I am able to compile the default settings for this model (CFG_PLL_FREQ == CFG_PLL_560_480_240), but when I change the CFG_PLL_FREQ to another value like CFG_PLL_566_450_225 in u-boot/include/configs/db12x.h the compilation fails:

board/ar7240/db12x/libdb12x.a(lowlevel_init_934x.o):/mnt/sda3/u-boot_mod-2014-11-19/u-boot/board/ar7240/db12x/../common/lowlevel_init_934x.S:81: undefined reference to `CPU_PLL_NFRAC_40'
board/ar7240/db12x/libdb12x.a(lowlevel_init_934x.o):/mnt/sda3/u-boot_mod-2014-11-19/u-boot/board/ar7240/db12x/../common/lowlevel_init_934x.S:82: undefined reference to `DDR_PLL_NFRAC_40'
board/ar7240/db12x/libdb12x.a(lowlevel_init_934x.o):/mnt/sda3/u-boot_mod-2014-11-19/u-boot/board/ar7240/db12x/../common/lowlevel_init_934x.S:89: undefined reference to `CPU_PLL_NFRAC_25'
board/ar7240/db12x/libdb12x.a(lowlevel_init_934x.o):/mnt/sda3/u-boot_mod-2014-11-19/u-boot/board/ar7240/db12x/../common/lowlevel_init_934x.S:90: undefined reference to `DDR_PLL_NFRAC_25'
Makefile:227: recipe for target 'bootstrap' failed

I would really appreciate a hint what goes wrong here. By the way, is it possible to test a overclocking configuration without flashing u-boot?

Hi,

I've ported u-boot_mod for the TL-WR1043ND v1:

https://github.com/ranma/u-boot_mod/tree/tl-wr1043nd

If you have one of these and you're feeling adventurous you can try this u-boot image (make sure you have a SPI-flasher just in case):
http://uguu.de/~ranma/openwrt/u-boot_mo … 1043nd.bin
To flash over serial using ymodem:

loady 0x81000000
cp.b 0xbf01fc00 0x8101fc00 0x400
erase 0xbf000000 +0x20000
cp.b 0x81000000 0xbf000000 0x20000

Note that the first "cp.b" copies the 0x400 bytes of mac address, board type and WPS pin so it is not lost.
Once this is done, you should see this on reboot:

*********************************************
*   U-Boot 1.1.4  (Oct 21 2015, 00:47:44)   *
*********************************************

AP83 (AR9132) U-Boot for TL-WR1043ND

DRAM:   
sri
32 MB
FLASH:  Winbond W25Q128 (16 MB)
CLOCKS: 400/400/200/25 MHz (CPU/RAM/AHB/SPI)

** Warning: using fixed MAC address!

LED on during eth initialization...

ag7100_enet_initialize...
No valid address in Flash. Using fixed address
: cfg1 0xf cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
eth0 up
Hit any key to stop autoboot: 1 0 

Booting image at: 0xBF020000

   Image name:   OpenWrt r46767
   Image type:   MIPS Linux Kernel Image (lzma compressed)
   Data size:    1106432 Bytes = 1.1 MB
   Load address: 0x80060000
   Entry point:  0x80060000

Uncompressing kernel image... OK!
Starting kernel...



OpenWrt kernel loader for AR7XXX/AR9XXX
Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
Looking for OpenWrt image... found at 0xbf022000
Decompressing kernel... done!
Starting kernel at 80060000...

[    0.000000] Linux version 3.18.20 (buildbot@builder1) (gcc version 4.8.3 (OpenWrt/Linaro GCC 4.8-2014.04 r46450) ) #1 Fri Sep 4 21:55:57 CEST 2015
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU0 revision is: 00019374 (MIPS 24Kc)
[    0.000000] SoC: Atheros AR9132 rev 2
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 02000000 @ 00000000 (usable)
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] Zone ranges:
[    0.000000]   Normal   [mem 0x00000000-0x01ffffff]
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x00000000-0x01ffffff]
[    0.000000] Initmem setup node 0 [mem 0x00000000-0x01ffffff]
[    0.000000] Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
[    0.000000] Primary data cache 32kB, 4-way, VIPT, cache aliases, linesize 32 bytes
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 8128
[    0.000000] Kernel command line:  board=TL-WR1043ND console=ttyS0,115200 rootfstype=squashfs,jffs2 noinitrd
[    0.000000] PID hash table entries: 128 (order: -3, 512 bytes)
[    0.000000] Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Writing ErrCtl register=00000000
[    0.000000] Readback ErrCtl register=00000000
[    0.000000] Memory: 28516K/32768K available (2621K kernel code, 129K rwdata, 344K rodata, 224K init, 194K bss, 4252K reserved)
[    0.000000] SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] NR_IRQS:51
[    0.000000] Clocks: CPU:400.000MHz, DDR:400.000MHz, AHB:200.000MHz, Ref:5.000MHz
[    0.000000] Calibrating delay loop... 265.42 BogoMIPS (lpj=1327104)
[    0.080000] pid_max: default: 32768 minimum: 301
[    0.080000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.090000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.100000] NET: Registered protocol family 16
[    0.100000] MIPS: machine is TP-LINK TL-WR1043ND
[    0.390000] Switched to clocksource MIPS
[    0.390000] NET: Registered protocol family 2
[    0.400000] TCP established hash table entries: 1024 (order: 0, 4096 bytes)
[    0.400000] TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
[    0.410000] TCP: Hash tables configured (established 1024 bind 1024)
[    0.420000] TCP: reno registered
[    0.420000] UDP hash table entries: 256 (order: 0, 4096 bytes)
[    0.420000] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[    0.430000] NET: Registered protocol family 1
[    0.440000] futex hash table entries: 256 (order: -1, 3072 bytes)
[    0.450000] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.460000] jffs2: version 2.2 (NAND) (SUMMARY) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
[    0.470000] msgmni has been set to 55
[    0.470000] io scheduler noop registered
[    0.480000] io scheduler deadline registered (default)
[    0.480000] Serial: 8250/16550 driver, 16 ports, IRQ sharing enabled
[    0.490000] console [ttyS0] disabled
[    0.520000] serial8250.0: ttyS0 at MMIO 0x18020000 (irq = 11, base_baud = 12500000) is a 16550A
[    0.520000] console [ttyS0] enabled
[    0.520000] console [ttyS0] enabled
[    0.530000] bootconsole [early0] disabled
[    0.530000] bootconsole [early0] disabled
[    0.540000] m25p80 spi0.0: found w25q128, expected m25p80
[    0.550000] m25p80 spi0.0: w25q128 (16384 Kbytes)
[    0.570000] 5 tp-link partitions found on MTD device spi0.0
[    0.570000] Creating 5 MTD partitions on "spi0.0":
[    0.580000] 0x000000000000-0x000000020000 : "u-boot"
[    0.580000] 0x000000020000-0x00000012e400 : "kernel"
[    0.590000] 0x00000012e400-0x000000ff0000 : "rootfs"
[    0.590000] mtd: device 2 (rootfs) set to be root filesystem
[    0.600000] 1 squashfs-split partitions found on MTD device rootfs
[    0.610000] 0x000000360000-0x000000ff0000 : "rootfs_data"
[    0.620000] 0x000000ff0000-0x000001000000 : "art"
[    0.620000] 0x000000020000-0x000000ff0000 : "firmware"
[    0.630000] Realtek RTL8366RB ethernet switch driver version 0.2.4
[    0.750000] rtl8366rb rtl8366rb: using GPIO pins 18 (SDA) and 19 (SCK)
[    0.750000] rtl8366rb rtl8366rb: RTL5937 ver. 3 chip found
[    0.940000] libphy: rtl8366rb: probed
[    1.260000] eth0: Atheros AG71xx at 0xb9000000, irq 4, mode:RGMII
[    1.260000] TCP: cubic registered
[    1.270000] NET: Registered protocol family 17
[    1.270000] bridge: automatic filtering via arp/ip/ip6tables has been deprecated. Update your scripts to load br_netfilter if you need this.
[    1.290000] Bridge firewalling registered
[    1.290000] 8021q: 802.1Q VLAN Support v1.8
[    1.300000] VFS: Mounted root (squashfs filesystem) readonly on device 31:2.
[    1.310000] Freeing unused kernel memory: 224K (80368000 - 803a0000)
[...]

Or if you hold QSS during power-on:

*********************************************
*   U-Boot 1.1.4  (Oct 21 2015, 00:47:44)   *
*********************************************

AP83 (AR9132) U-Boot for TL-WR1043ND

DRAM:   
sri
32 MB
FLASH:  Winbond W25Q128 (16 MB)
CLOCKS: 400/400/200/25 MHz (CPU/RAM/AHB/SPI)

** Warning: using fixed MAC address!

LED on during eth initialization...

ag7100_enet_initialize...
No valid address in Flash. Using fixed address
: cfg1 0xf cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
eth0 up
Press reset button for at least:
- 3 sec. to run web failsafe mode
- 5 sec. to run U-Boot console
- 7 sec. to run U-Boot netconsole

Reset button is pressed for:  3 

Button was pressed for 3 sec...
HTTP server is starting for firmware update...

dup 1 speed 1000
HTTP server is starting at IP: 192.168.0.2
HTTP server is ready!

Have fun!

@moroboshi great job!

pepe2k wrote:

@moroboshi great job!

I've sent you a pull request for some prework as https://github.com/pepe2k/u-boot_mod/pull/82

One peculiarity with the older AR9132 seems to be that cached read accesses to the SPI flash don't seem to work,
so the uncached range has to be used (0x9f000000 (cached) vs 0xbf000000 (uncached)).

moroboshi wrote:
pepe2k wrote:

@moroboshi great job!

I've sent you a pull request for some prework as https://github.com/pepe2k/u-boot_mod/pull/82

One peculiarity with the older AR9132 seems to be that cached read accesses to the SPI flash don't seem to work,
so the uncached range has to be used (0x9f000000 (cached) vs 0xbf000000 (uncached)).

I'm finishing some commercial project till end of this month and then I hope to have some time for all waiting pull requests.

Somewhat related: While hacking I accidentally wrote a bad version to the flash and got annoyed that you can't easily debrick over JTAG (unless you can make the RAM initialization work and upload a RAM u-boot) and my SPI-programmer didn't seem to work for in-circuit programming (tried keeping the CPU in reset, didn't help).

Since I didn't want to unsolder the SPI I looked into adding the proper necessary flash programming support to OpenOCD and now have this (@500kHz JTAG clock, doesn't get much faster with higher clocks since with the FTDI2232 the effective speed is severly limited by the USB round-trip-time):

$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> flash write_image unlock /tmp/test.bin 0xbf040000 
auto unlock enabled
read_flash_id: 1840ef
Found flash device 'win w25q128' (ID 0x001840ef)
writing 256 bytes to flash page @0x00000000
writing 256 bytes to flash page @0x00000100
writing 256 bytes to flash page @0x00000200
writing 256 bytes to flash page @0x00000300
[...]
writing 256 bytes to flash page @0x0001e800
writing 256 bytes to flash page @0x0001e900
writing 256 bytes to flash page @0x0001ea00
writing 256 bytes to flash page @0x0001eb00
wrote 131072 bytes from file /tmp/u-boot_mod.20151021.tl-wr1043nd.bin in 1678.842163s (0.076 KiB/s)

Necessary openocd.cfg setting:

flash bank ath79 ath79 0xbf000000 0x01000000 0 0 $_TARGETNAME

diffstat:

 Makefile.am |    1 
 ath79.c     |  807 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers.c   |    2 
 spi.c       |    1 
 4 files changed, 811 insertions(+)

Patch:

diff -x minidriver_imp.h -x xscale_debug.inc -x startup_tcl.inc -x startup.tcl -x openocd -x Makefile.in -x Makefile -Nru openocd-0.9.0-vanilla/src/flash/nor/ath79.c openocd-0.9.0/src/flash/nor/ath79.c
--- openocd-0.9.0-vanilla/src/flash/nor/ath79.c    1970-01-01 01:00:00.000000000 +0100
+++ openocd-0.9.0/src/flash/nor/ath79.c    2015-10-24 19:08:21.814867892 +0200
@@ -0,0 +1,807 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Antonio Borneo <borneo.antonio@gmail.com>       *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ ***************************************************************************/
+
+/* STM Serial Memory Interface (SMI) controller is a SPI bus controller
+ * specifically designed for SPI memories.
+ * Only SPI "mode 3" (CPOL=1 and CPHA=1) is supported.
+ * Two working modes are available:
+ * - SW mode: the SPI is controlled by SW. Any custom commands can be sent
+ *   on the bus.
+ * - HW mode: the SPI but is under SMI control. Memory content is directly
+ *   accessible in CPU memory space. CPU can read, write and execute memory
+ *   content. */
+
+/* ATTENTION:
+ * To have flash memory mapped in CPU memory space, the SMI controller
+ * have to be in "HW mode". This requires following constraints:
+ * 1) The command "reset init" have to initialize SMI controller and put
+ *    it in HW mode;
+ * 2) every command in this file have to return to prompt in HW mode. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include "spi.h"
+#include <jtag/jtag.h>
+#include <helper/time_support.h>
+#include <target/mips32.h>
+#include <target/mips32_pracc.h>
+
+#define ATH79_REG_FS     0
+#define ATH79_REG_CLOCK  4
+#define ATH79_REG_WRITE  8
+#define ATH79_REG_DATA  12
+
+#define AR7240_SPI_CS_DIS  0xf0000
+#define AR7240_SPI_CE_LOW  0x60000
+#define AR7240_SPI_CE_HIGH 0x60100
+
+#define SMI_READ_REG(a) (0)
+#define SMI_WRITE_REG(a, v) (0)
+
+#define READ_REG(a) (_READ_REG(a))
+#define _READ_REG(a)            \
+{                                    \
+    int __a;                        \
+    uint32_t __v;                    \
+                                    \
+    __a = target_read_u32(target, io_base + (a), &__v); \
+    if (__a != ERROR_OK)            \
+        return __a;                    \
+    return __v;                            \
+}
+
+#define WRITE_REG(a, v)            \
+{                                    \
+    int __r;                        \
+                                    \
+    __r = target_write_u32(target, io_base + (a), (v)); \
+    if (__r != ERROR_OK)            \
+        return __r;                    \
+}
+
+#if 0
+static uint32_t ath79_read_reg(struct target *target, uint32_t reg) {
+    uint32_t v = 0;
+    int err = target_read_u32(target, 0xbf000000 + reg, &v);
+    if (err != ERROR_OK) {
+        LOG_ERROR("target_read_u32 failed");
+    }
+    return v;
+}
+
+static void ath79_write_reg(struct target *target, uint32_t reg, uint32_t value) {
+    int err = target_write_u32(target, 0xbf000000 + reg, value);
+    if (err != ERROR_OK) {
+        LOG_ERROR("target_read_u32 failed");
+    }
+}
+#endif
+
+static int ath79_spi_bitbang_bytes(struct target *target, uint32_t io_base,
+                   int pre_deselect, int post_deselect,
+                   uint8_t *data, int len) {
+    LOG_DEBUG("ath79_spi_bitbang_bytes(%p, %08x, %d, %d, %p, %d)",
+        target, io_base, pre_deselect, post_deselect, data, len);
+    struct mips32_common *mips32 = target_to_mips32(target);
+    struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
+
+    uint32_t pracc_out = 0;
+
+    const int pracc_pre_post = 26;
+    const int pracc_loop_byte = 8 * 2 + 2;
+
+    struct pracc_queue_info ctx = {.max_code = pracc_pre_post + len * pracc_loop_byte};
+    uint32_t *out = malloc(len + 3);
+    memset(out, 0xa5, len + 3);
+
+    pracc_queue_init(&ctx);
+    if (ctx.retval != ERROR_OK)
+        goto exit;
+
+    /* Calculate largest len given the available instruction budget. */
+    const int max_len = (PRACC_OUT_OFFSET / 4 - pracc_pre_post) / pracc_loop_byte;
+    if (len > max_len) {
+        LOG_INFO("len too big: %d > %d", len, max_len);
+        ctx.retval = ERROR_BUF_TOO_SMALL;
+        goto exit;
+    } else {
+        LOG_DEBUG("max len %d. len %d => max code %d", max_len, len, ctx.max_code);
+    }
+
+    pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR));  /* $15 = MIPS32_PRACC_BASE_ADDR */
+    pracc_add(&ctx, 0, MIPS32_LUI(1, UPPER16(io_base)));   /* $1 = io_base */
+    if (pre_deselect) {
+        /* [$1 + FS] = 1  (enable flash io register access) */
+        pracc_add(&ctx, 0, MIPS32_LUI(2, UPPER16(1)));
+        pracc_add(&ctx, 0, MIPS32_ORI(2, 2, LOWER16(1)));
+        pracc_add(&ctx, 0, MIPS32_SW(2, ATH79_REG_FS, 1));
+        /* deselect flash just in case */
+        pracc_add(&ctx, 0, MIPS32_LUI(2, UPPER16(AR7240_SPI_CS_DIS))); /* $2 = SPI_CS_DIS */
+        pracc_add(&ctx, 0, MIPS32_ORI(2, 2, LOWER16(AR7240_SPI_CS_DIS))); /* $2 = SPI_CS_DIS */
+        pracc_add(&ctx, 0, MIPS32_SW(2, ATH79_REG_WRITE, 1));  /* [$1 + WRITE] = $2 */
+    }
+    /* t0 = CLOCK_LOW + 0-bit */
+    pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16((AR7240_SPI_CE_LOW + 0))));
+    pracc_add(&ctx, 0, MIPS32_ORI(8, 8, LOWER16((AR7240_SPI_CE_LOW + 0))));
+    /* t1 = CLOCK_LOW + 1-bit */
+    pracc_add(&ctx, 0, MIPS32_LUI(9, UPPER16((AR7240_SPI_CE_LOW + 1))));
+    pracc_add(&ctx, 0, MIPS32_ORI(9, 9, LOWER16((AR7240_SPI_CE_LOW + 1))));
+    /* t2 = CLOCK_HIGH + 0-bit */
+    pracc_add(&ctx, 0, MIPS32_LUI(10, UPPER16((AR7240_SPI_CE_HIGH + 0))));
+    pracc_add(&ctx, 0, MIPS32_ORI(10, 10, LOWER16((AR7240_SPI_CE_HIGH + 0))));
+    /* t2 = CLOCK_HIGH + 1-bit */
+    pracc_add(&ctx, 0, MIPS32_LUI(11, UPPER16((AR7240_SPI_CE_HIGH + 1))));
+    pracc_add(&ctx, 0, MIPS32_ORI(11, 11, LOWER16((AR7240_SPI_CE_HIGH + 1))));
+
+    for (int i = 0; i < len; i++) {
+        uint8_t x = data[i];
+        LOG_DEBUG("%d: generating code for %02x", i, x);
+        for (int j=0; j<8; j++) {
+            int bit = ((x << j) & 0x80) == 0x80;
+
+            LOG_DEBUG("  %d: generating code for bit %d", j, bit);
+            if (bit) {
+                /* [$1 + WRITE] = t1 */
+                pracc_add(&ctx, 0, MIPS32_SW(9, ATH79_REG_WRITE, 1));
+                /* [$1 + WRITE] = t3 */
+                pracc_add(&ctx, 0, MIPS32_SW(11, ATH79_REG_WRITE, 1));
+            } else {
+                /* [$1 + WRITE] = t0 */
+                pracc_add(&ctx, 0, MIPS32_SW(8, ATH79_REG_WRITE, 1));
+                /* [$1 + WRITE] = t2 */
+                pracc_add(&ctx, 0, MIPS32_SW(10, ATH79_REG_WRITE, 1));
+            }
+        }
+        if (i % 4 == 3) {
+            /* $3 = [$1 + DATA] */
+            pracc_add(&ctx, 0, MIPS32_LW(3, ATH79_REG_DATA, 1)); /* $3 = [$1 + DATA] */
+            /* [OUTi] = $3 */
+            pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + pracc_out,
+                    MIPS32_SW(3, PRACC_OUT_OFFSET + pracc_out, 15));
+            pracc_out += 4;
+        }
+    }
+    if (len & 3) { /* not a multiple of 4 bytes */
+        /* $3 = [$1 + DATA] */
+        pracc_add(&ctx, 0, MIPS32_LW(3, ATH79_REG_DATA, 1)); /* $3 = [$1 + DATA] */
+        /* [OUTi] = $3 */
+        pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + pracc_out,
+                MIPS32_SW(3, PRACC_OUT_OFFSET + pracc_out, 15));
+        pracc_out += 4;
+    }
+
+    if (post_deselect) {
+        pracc_add(&ctx, 0, MIPS32_LUI(2, UPPER16(AR7240_SPI_CS_DIS))); /* $2 = SPI_CS_DIS */
+        pracc_add(&ctx, 0, MIPS32_ORI(2, 2, LOWER16(AR7240_SPI_CS_DIS))); /* $2 = SPI_CS_DIS */
+        pracc_add(&ctx, 0, MIPS32_SW(2, ATH79_REG_WRITE, 1));  /* [$1 + WRITE] = $2 */
+
+        /* [$1 + FS] = 0  (disable flash io register access) */
+        pracc_add(&ctx, 0, MIPS32_XORI(2, 2, 0));
+        pracc_add(&ctx, 0, MIPS32_SW(2, ATH79_REG_FS, 1));
+    }
+
+    /* common pracc epilogue */
+    pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1)));            /* jump to start */
+    pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0));                    /* restore $15 from DeSave */
+
+    LOG_DEBUG("Assembled %d instructions, %d stores:", ctx.code_count, ctx.store_count);
+    for (int i = 0; i < ctx.code_count; i++) {
+        LOG_DEBUG("%08x", ctx.pracc_list[i]);
+    }
+
+    ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, out);
+    if (ctx.retval != ERROR_OK)
+        goto exit;
+
+    int pracc_words = pracc_out / 4;
+    if (len & 3) { /* not a multiple of 4 bytes */
+        /* Need to realign last word. */
+        out[pracc_words - 1] <<= 8 * (4 - len);
+    }
+    if (1234 != ntohl(1234)) {
+        /* byteswap buffer */
+        for (int i = 0; i < pracc_words; i++) {
+            out[i] = ntohl(out[i]);
+        }
+    }
+    for (int i = 0; i < len; i++) {
+        LOG_DEBUG("bitbang %02x => %02x",
+            data[i], ((uint8_t*)out)[i]);
+    }
+    memcpy(data, out, len);
+
+exit:
+    if (ctx.retval != ERROR_OK) {
+        memset(data, 0x5a, len);
+    }
+    if (out != NULL)
+        free(out);
+    pracc_queue_free(&ctx);
+    return ctx.retval;
+}
+
+#define SMI_POLL_TFF(timeout)
+#define SMI_SET_SW_MODE()
+#define SMI_SET_HWWB_MODE()
+#define SMI_SET_HW_MODE()
+#define SMI_CLEAR_TFF()
+
+#define SMI_BANK_SIZE      (0x01000000)
+
+#define SMI_CR1 (0x00) /* Control register 1 */
+#define SMI_CR2 (0x04) /* Control register 2 */
+#define SMI_SR  (0x08) /* Status register */
+#define SMI_TR  (0x0c) /* TX */
+#define SMI_RR  (0x10) /* RX */
+
+/* fields in SMI_CR1 */
+#define SMI_SW_MODE       0x10000000 /* set to enable SW Mode */
+#define SMI_WB_MODE       0x20000000 /* Write Burst Mode */
+
+/* fields in SMI_CR2 */
+#define SMI_TX_LEN_1      0x00000001 /* data length = 1 byte */
+#define SMI_TX_LEN_4      0x00000004 /* data length = 4 byte */
+#define SMI_RX_LEN_3      0x00000030 /* data length = 3 byte */
+#define SMI_SEND          0x00000080 /* Send data */
+#define SMI_RSR           0x00000400 /* reads status reg */
+#define SMI_WE            0x00000800 /* Write Enable */
+#define SMI_SEL_BANK0     0x00000000 /* Select Bank0 */
+#define SMI_SEL_BANK1     0x00001000 /* Select Bank1 */
+#define SMI_SEL_BANK2     0x00002000 /* Select Bank2 */
+#define SMI_SEL_BANK3     0x00003000 /* Select Bank3 */
+
+/* fields in SMI_SR */
+#define SMI_TFF           0x00000100 /* Transfer Finished Flag */
+
+/* Commands */
+#define SMI_READ_ID       0x0000009F /* Read Flash Identification */
+
+/* Timeout in ms */
+#define SMI_CMD_TIMEOUT   (100)
+#define SMI_PROBE_TIMEOUT (100)
+#define SMI_MAX_TIMEOUT  (3000)
+
+struct ath79_flash_bank {
+    int probed;
+    uint32_t io_base;
+    uint32_t bank_num;
+    const struct flash_device *dev;
+};
+
+struct ath79_target {
+    char *name;
+    uint32_t tap_idcode;
+    uint32_t smi_base;
+    uint32_t io_base;
+};
+
+static const struct ath79_target target_devices[] = {
+    /* name,          tap_idcode, smi_base,   io_base */
+    { "ATH79",        0x00000001, 0xbf000000, 0xbf000000 },
+    { NULL,           0,          0,          0 }
+};
+
+FLASH_BANK_COMMAND_HANDLER(ath79_flash_bank_command)
+{
+    struct ath79_flash_bank *ath79_info;
+
+    LOG_DEBUG("%s", __func__);
+
+    if (CMD_ARGC < 6)
+        return ERROR_COMMAND_SYNTAX_ERROR;
+
+    ath79_info = malloc(sizeof(struct ath79_flash_bank));
+    if (ath79_info == NULL) {
+        LOG_ERROR("not enough memory");
+        return ERROR_FAIL;
+    }
+
+    bank->driver_priv = ath79_info;
+    ath79_info->probed = 0;
+
+    return ERROR_OK;
+}
+
+/* Read the status register of the external SPI flash chip.
+ * The operation is triggered by setting SMI_RSR bit.
+ * SMI sends the proper SPI command (0x05) and returns value in SMI_SR */
+static int read_status_reg(struct flash_bank *bank, uint32_t *status)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    uint32_t io_base = ath79_info->io_base;
+
+    uint8_t spi_bytes[] = {SPIFLASH_READ_STATUS, 0};
+
+    /* Send SPI command "read STATUS" */
+    int retval = ath79_spi_bitbang_bytes(
+        target, io_base, 1, 1, spi_bytes, sizeof(spi_bytes));
+
+    *status = spi_bytes[1];
+
+    return retval;
+}
+
+/* check for WIP (write in progress) bit in status register */
+/* timeout in ms */
+static int wait_till_ready(struct flash_bank *bank, int timeout)
+{
+    uint32_t status;
+    int retval;
+    long long endtime;
+
+    endtime = timeval_ms() + timeout;
+    do {
+        /* read flash status register */
+        retval = read_status_reg(bank, &status);
+        if (retval != ERROR_OK)
+            return retval;
+
+        if ((status & SPIFLASH_BSY_BIT) == 0)
+            return ERROR_OK;
+        alive_sleep(1);
+    } while (timeval_ms() < endtime);
+
+    LOG_ERROR("timeout");
+    return ERROR_FAIL;
+}
+
+/* Send "write enable" command to SPI flash chip.
+ * The operation is triggered by setting SMI_WE bit, and SMI sends
+ * the proper SPI command (0x06) */
+static int smi_write_enable(struct flash_bank *bank)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    uint32_t io_base = ath79_info->io_base;
+    uint32_t status;
+    int retval;
+
+    uint8_t spi_bytes[] = {SPIFLASH_WRITE_ENABLE};
+
+    /* Send SPI command "write enable" */
+    retval = ath79_spi_bitbang_bytes(
+        target, io_base, 1, 1, spi_bytes, sizeof(spi_bytes));
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* read flash status register */
+    retval = read_status_reg(bank, &status);
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* Check write enabled */
+    if ((status & SPIFLASH_WE_BIT) == 0) {
+        LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status);
+        return ERROR_FAIL;
+    }
+
+    return ERROR_OK;
+}
+
+static int erase_command(struct flash_bank *bank, int sector)
+{
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    struct target *target = bank->target;
+    uint32_t io_base = ath79_info->io_base;
+    uint32_t offset = bank->sectors[sector].offset;
+
+    uint8_t spi_bytes[] = {
+        ath79_info->dev->erase_cmd,
+        offset >> 16,
+        offset >> 8,
+        offset
+    };
+
+    /* bitbang command */
+    return ath79_spi_bitbang_bytes(
+        target, io_base, 1, 1, spi_bytes, sizeof(spi_bytes));
+}
+
+static int smi_erase_sector(struct flash_bank *bank, int sector)
+{
+    int retval;
+
+    retval = smi_write_enable(bank);
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* send SPI command "block erase" */
+    retval = erase_command(bank, sector);
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* poll WIP for end of self timed Sector Erase cycle */
+    retval = wait_till_ready(bank, SMI_MAX_TIMEOUT);
+    if (retval != ERROR_OK)
+        return retval;
+
+    return ERROR_OK;
+}
+
+static int ath79_erase(struct flash_bank *bank, int first, int last)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    int retval = ERROR_OK;
+    int sector;
+
+    LOG_DEBUG("%s: from sector %d to sector %d", __func__, first, last);
+
+    if (target->state != TARGET_HALTED) {
+        LOG_ERROR("Target not halted");
+        return ERROR_TARGET_NOT_HALTED;
+    }
+
+    if ((first < 0) || (last < first) || (last >= bank->num_sectors)) {
+        LOG_ERROR("Flash sector invalid");
+        return ERROR_FLASH_SECTOR_INVALID;
+    }
+
+    if (!(ath79_info->probed)) {
+        LOG_ERROR("Flash bank not probed");
+        return ERROR_FLASH_BANK_NOT_PROBED;
+    }
+
+    for (sector = first; sector <= last; sector++) {
+        if (bank->sectors[sector].is_protected) {
+            LOG_ERROR("Flash sector %d protected", sector);
+            return ERROR_FAIL;
+        }
+    }
+
+    for (sector = first; sector <= last; sector++) {
+        retval = smi_erase_sector(bank, sector);
+        if (retval != ERROR_OK)
+            break;
+        keep_alive();
+    }
+
+    /* Switch to HW mode before return to prompt */
+    SMI_SET_HW_MODE();
+    return retval;
+}
+
+static int ath79_protect(struct flash_bank *bank, int set,
+    int first, int last)
+{
+    int sector;
+
+    for (sector = first; sector <= last; sector++)
+        bank->sectors[sector].is_protected = set;
+    return ERROR_OK;
+}
+
+static int ath79_write_page(struct flash_bank *bank, const uint8_t *buffer,
+    uint32_t address, uint32_t len)
+{
+    uint8_t spi_page_buf[0x100];
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    struct target *target = bank->target;
+    uint32_t io_base = ath79_info->io_base;
+    uint32_t written = 0;
+    uint8_t spi_cmd[] = {
+        SPIFLASH_PAGE_PROGRAM,
+        address >> 16,
+        address >> 8,
+        address,
+    };
+    int retval;
+
+    if (address & 0xff) {
+        LOG_ERROR("ath79_write_page: unaligned write address: %08x\n", address);
+        return ERROR_FAIL;
+    }
+
+    if (len > sizeof(spi_page_buf)) {
+        LOG_ERROR("ath79_write_page: length bigger than page size %ld: %d\n",
+            sizeof(spi_page_buf), len);
+        return ERROR_FAIL;
+    }
+
+    uint32_t i;
+    for (i = 0; i < len; i++) {
+        if (buffer[i] != 0xff)
+            break;
+    }
+    if (i == len)  /* all 0xff, no need to program. */
+        return ERROR_OK;
+
+    address -= ath79_info->io_base;
+
+    LOG_INFO("writing %d bytes to flash page @0x%08x", len, address);
+
+    memcpy(spi_page_buf, buffer, len);
+
+    /* unlock writes */
+    retval = smi_write_enable(bank);
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* bitbang command */
+    retval = ath79_spi_bitbang_bytes(
+        target, io_base, 1, 0, spi_cmd, sizeof(spi_cmd));
+    if (retval != ERROR_OK)
+        return retval;
+
+    /* Length limit derived from pracc code size limit */
+    const uint32_t spi_max_len = 112;
+
+    while (len > spi_max_len) {
+        /* write blocks with len limited by pracc code size */
+        retval = ath79_spi_bitbang_bytes(
+            target, io_base, 0, 0, &spi_page_buf[written], spi_max_len);
+        if (retval != ERROR_OK)
+            return retval;
+
+        written += spi_max_len;
+        len -= spi_max_len;
+    }
+
+    /* write final block of data */
+    return ath79_spi_bitbang_bytes(
+        target, io_base, 0, 1, &spi_page_buf[written], len);
+}
+
+static int smi_write_buffer(struct flash_bank *bank, const uint8_t *buffer,
+    uint32_t address, uint32_t len)
+{
+    int retval;
+
+    LOG_DEBUG("%s: address=0x%08" PRIx32 " len=0x%08" PRIx32,
+            __func__, address, len);
+
+    while (len > 0) {
+        const uint32_t page_size = 0x100;
+
+        /* Page size is 256 bytes */
+        int page_len = len > 0x100 ? 0x100 : len;
+        retval = ath79_write_page(
+            bank, buffer, address, page_len);
+        if (retval != ERROR_OK)
+            return retval;
+
+        buffer += page_size;
+        address += page_size;
+        len -= page_len;
+    }
+
+    return ERROR_OK;
+}
+
+static int ath79_write(struct flash_bank *bank, const uint8_t *buffer,
+    uint32_t offset, uint32_t count)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    int sector;
+
+    LOG_DEBUG("%s: offset=0x%08" PRIx32 " count=0x%08" PRIx32,
+        __func__, offset, count);
+
+    if (target->state != TARGET_HALTED) {
+        LOG_ERROR("Target not halted");
+        return ERROR_TARGET_NOT_HALTED;
+    }
+
+    if (offset + count > ath79_info->dev->size_in_bytes) {
+        LOG_WARNING("Write pasts end of flash. Extra data discarded.");
+        count = ath79_info->dev->size_in_bytes - offset;
+    }
+
+    /* Check sector protection */
+    for (sector = 0; sector < bank->num_sectors; sector++) {
+        /* Start offset in or before this sector? */
+        /* End offset in or behind this sector? */
+        if ((offset <
+                (bank->sectors[sector].offset + bank->sectors[sector].size))
+            && ((offset + count - 1) >= bank->sectors[sector].offset)
+            && bank->sectors[sector].is_protected) {
+            LOG_ERROR("Flash sector %d protected", sector);
+            return ERROR_FAIL;
+        }
+    }
+
+    return smi_write_buffer(bank, buffer, bank->base + offset, count);
+}
+
+/* Return ID of flash device */
+/* On exit, SW mode is kept */
+static int read_flash_id(struct flash_bank *bank, uint32_t *id)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    uint32_t io_base = ath79_info->io_base;
+    int retval;
+    uint8_t spi_bytes[] = {SPIFLASH_READ_ID, 0, 0, 0, 0, 0, 0, 0};
+    /* Read 8 bytes @0xbf060000 */
+    uint8_t spi_test[] = {3, 6, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0};
+    uint8_t spi_buf[12];
+
+    if (target->state != TARGET_HALTED) {
+        LOG_ERROR("Target not halted");
+        return ERROR_TARGET_NOT_HALTED;
+    }
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 5);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 6);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 7);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 8);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 9);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 10);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 11);
+
+    memcpy(spi_buf, spi_test, sizeof(spi_test));
+    ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+            spi_buf, 12);
+
+    /* Send SPI command "read ID" */
+    retval = ath79_spi_bitbang_bytes(target, io_base, 1, 1,
+                spi_bytes, sizeof(spi_bytes));
+    if (retval != ERROR_OK)
+        return retval;
+
+    *id = (spi_bytes[1] << 0)
+        | (spi_bytes[2] << 8)
+        | (spi_bytes[3] << 16);
+    LOG_ERROR("read_flash_id: %06x", *id);
+
+    return ERROR_OK;
+}
+
+static int ath79_probe(struct flash_bank *bank)
+{
+    struct target *target = bank->target;
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    uint32_t io_base;
+    struct flash_sector *sectors;
+    uint32_t id = 0; /* silence uninitialized warning */
+    const struct ath79_target *target_device;
+    int retval;
+
+    if (ath79_info->probed)
+        free(bank->sectors);
+    ath79_info->probed = 0;
+
+    for (target_device = target_devices ; target_device->name ; ++target_device)
+        if (target_device->tap_idcode == target->tap->idcode)
+            break;
+    if (!target_device->name) {
+        LOG_ERROR("Device ID 0x%" PRIx32 " is not known as SMI capable",
+                target->tap->idcode);
+        return ERROR_FAIL;
+    }
+
+    io_base = target_device->io_base;
+    ath79_info->io_base = io_base;
+
+    LOG_DEBUG("Valid SMI on device %s at address 0x%" PRIx32,
+        target_device->name, bank->base);
+
+    /* read and decode flash ID; returns in SW mode */
+    retval = read_flash_id(bank, &id);
+    SMI_SET_HW_MODE();
+    if (retval != ERROR_OK)
+        return retval;
+
+    ath79_info->dev = NULL;
+    for (const struct flash_device *p = flash_devices; p->name ; p++)
+        if (p->device_id == id) {
+            ath79_info->dev = p;
+            break;
+        }
+
+    if (!ath79_info->dev) {
+        LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id);
+        return ERROR_FAIL;
+    }
+
+    LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")",
+        ath79_info->dev->name, ath79_info->dev->device_id);
+
+    /* Set correct size value */
+    bank->size = ath79_info->dev->size_in_bytes;
+
+    /* create and fill sectors array */
+    bank->num_sectors =
+        ath79_info->dev->size_in_bytes / ath79_info->dev->sectorsize;
+    sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+    if (sectors == NULL) {
+        LOG_ERROR("not enough memory");
+        return ERROR_FAIL;
+    }
+
+    for (int sector = 0; sector < bank->num_sectors; sector++) {
+        sectors[sector].offset = sector * ath79_info->dev->sectorsize;
+        sectors[sector].size = ath79_info->dev->sectorsize;
+        sectors[sector].is_erased = -1;
+        sectors[sector].is_protected = 1;
+    }
+
+    bank->sectors = sectors;
+    ath79_info->probed = 1;
+    return ERROR_OK;
+}
+
+static int ath79_auto_probe(struct flash_bank *bank)
+{
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+    if (ath79_info->probed)
+        return ERROR_OK;
+    return ath79_probe(bank);
+}
+
+static int ath79_protect_check(struct flash_bank *bank)
+{
+    /* Nothing to do. Protection is only handled in SW. */
+    return ERROR_OK;
+}
+
+static int get_ath79_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+    struct ath79_flash_bank *ath79_info = bank->driver_priv;
+
+    if (!(ath79_info->probed)) {
+        snprintf(buf, buf_size,
+            "\nSMI flash bank not probed yet\n");
+        return ERROR_OK;
+    }
+
+    snprintf(buf, buf_size, "\nSMI flash information:\n"
+        "  Device \'%s\' (ID 0x%08" PRIx32 ")\n",
+        ath79_info->dev->name, ath79_info->dev->device_id);
+
+    return ERROR_OK;
+}
+
+struct flash_driver ath79_flash = {
+    .name = "ath79",
+    .flash_bank_command = ath79_flash_bank_command,
+    .erase = ath79_erase,
+    .protect = ath79_protect,
+    .write = ath79_write,
+    .read = default_flash_read,
+    .probe = ath79_probe,
+    .auto_probe = ath79_auto_probe,
+    .erase_check = default_flash_blank_check,
+    .protect_check = ath79_protect_check,
+    .info = get_ath79_info,
+};
diff -x minidriver_imp.h -x xscale_debug.inc -x startup_tcl.inc -x startup.tcl -x openocd -x Makefile.in -x Makefile -Nru openocd-0.9.0-vanilla/src/flash/nor/drivers.c openocd-0.9.0/src/flash/nor/drivers.c
--- openocd-0.9.0-vanilla/src/flash/nor/drivers.c    2015-04-24 17:09:54.000000000 +0200
+++ openocd-0.9.0/src/flash/nor/drivers.c    2015-10-23 09:15:35.390154487 +0200
@@ -22,6 +22,7 @@
 #endif
 #include "imp.h"
 
+extern struct flash_driver ath79_flash;
 extern struct flash_driver lpc2000_flash;
 extern struct flash_driver lpc288x_flash;
 extern struct flash_driver lpc2900_flash;
@@ -65,6 +66,7 @@
  * @todo Make this dynamically extendable with loadable modules.
  */
 static struct flash_driver *flash_drivers[] = {
+    &ath79_flash,
     &lpc2000_flash,
     &lpc288x_flash,
     &lpc2900_flash,
diff -x minidriver_imp.h -x xscale_debug.inc -x startup_tcl.inc -x startup.tcl -x openocd -x Makefile.in -x Makefile -Nru openocd-0.9.0-vanilla/src/flash/nor/Makefile.am openocd-0.9.0/src/flash/nor/Makefile.am
--- openocd-0.9.0-vanilla/src/flash/nor/Makefile.am    2015-04-24 17:09:54.000000000 +0200
+++ openocd-0.9.0/src/flash/nor/Makefile.am    2015-10-23 09:14:52.291677642 +0200
@@ -29,6 +29,7 @@
     pic32mx.c \
     spi.c \
     stmsmi.c \
+    ath79.c \
     stellaris.c \
     stm32f1x.c \
     stm32f2x.c \
diff -x minidriver_imp.h -x xscale_debug.inc -x startup_tcl.inc -x startup.tcl -x openocd -x Makefile.in -x Makefile -Nru openocd-0.9.0-vanilla/src/flash/nor/spi.c openocd-0.9.0/src/flash/nor/spi.c
--- openocd-0.9.0-vanilla/src/flash/nor/spi.c    2015-04-24 17:09:54.000000000 +0200
+++ openocd-0.9.0/src/flash/nor/spi.c    2015-10-23 10:03:08.459651011 +0200
@@ -74,6 +74,7 @@
     FLASH_ID("win w25q32fv",   0xd8, 0xc7, 0x001640ef, 0x100, 0x10000, 0x400000),
     FLASH_ID("win w25q32dw",   0xd8, 0xc7, 0x001660ef, 0x100, 0x10000, 0x400000),
     FLASH_ID("win w25q64cv",   0xd8, 0xc7, 0x001740ef, 0x100, 0x10000, 0x800000),
+    FLASH_ID("win w25q128",    0xd8, 0xc7, 0x001840ef, 0x100, 0x10000, 0x1000000),
     FLASH_ID("gd gd25q20",     0x20, 0xc7, 0x00c84012, 0x100, 0x1000, 0x80000),
     FLASH_ID(NULL,             0,    0,       0,          0,     0,       0)
 };

Hey i have installed This uboot mod
I dont have backup of original u-boot
i want to revert back to orignal Tplink uboot & Official FW
with this uboot i can't revert back to orignal firmware.
please tell me how i can produce a uboot.bin from official firmware image??
or can you please uploade a backup of orignal uboot.bin for TL-WR740n V4.27
Thanks

Shahid wrote:

Hey i have installed This uboot mod
I dont have backup of original u-boot
i want to revert back to orignal Tplink uboot & Official FW
with this uboot i can't revert back to orignal firmware.
please tell me how i can produce a uboot.bin from official firmware image??
or can you please uploade a backup of orignal uboot.bin for TL-WR740n V4.27
Thanks

https://github.com/pepe2k/u-boot_mod/tr … oot_images

Can you please send me a pre-compiled version of this for the tl-wr841n v9? I'd like to try it.

Hi,pepe2k:
I want Use USB to update the firmware ,How can I  add USB to the u-boot ??