Ever since I saw a YDPG18A mod post where flash was upgraded, I've been curious about upgrading NAND flash. After I started investigating I realized the upgrade mentioned wouldn't work, so I called the guy on it and he mentioned it didn't work, just that he hadn't gotten around to posting about it. If he'd found the exact same model flash chip as the existing one, soldered it on, and reflashed, it would have worked. But a lot of the flash chips used in these devices are old and hard to get ahold of. Luckily, they use a standard footprint (TSOP48), and a standard pinout used for SLC and MLC NAND flash.
When a system is flashed using LiveSuit, LiveSuit first sends over some modules located within the flash image file to load into system memory to assist with the flashing. One of these modules partitions and formats the NAND flash - then individual blobs of data are sent for each partition. The thing about these flash chips is that even though they often come in a TSOP48 package, they all have different model numbers, which can be queried from the chip. They also have different geometry - number of dies in one chip, number of sectors, number of blocks, and page size. The driver only supports the same model of flash for every flash chip the board is populated with - otherwise it will ignore everything but the first chip.
AllWinner has a generic NAND flash driver which is compiled into Android, the bootloader's sprite.axf and U-Boot (on 4.x devices), the generic 'boot' image that jumps to the bootloader, and the binaries that LiveSuit sends over to assist with flashing the device during a flash. This generic driver contains several tables, one for each vendor of NAND supported, containing each supported chip ID, and the geoemetry of those chips. You can see the list here: https://raw.github.com/amery/linux-allwinner/allwinner-v3.0-android-v2/drivers/block/sun4i_nand/src/scan/nand_id.c
- it's a short list. The largest flash chips supported are 16GB, and every one I've looked for so far has been hard to get ahold of.
So what I figured I'd do is get an understanding of how the NAND driver works, add support for a new chip, remove the old NAND and solder in the new chip(s), and flash the device. I wanted to try maxng out the possible storage - the largest TSOP48 flash you can get is 32 gigabytes. So I purchased some flash from Avnet - a couple of the Micron MT29F256G08CJAAAWP. (256 gigabit chips, meaning 32 gigabytes). This chip isn't in the Micron table in the AllWinner NAND driver. Worse, I found out I had to add support to all of the modules I mentioned above - even though they use the same NAND driver, source code is not available for them so it's not possible to rebuild them.
Desoldering the old flash was something I had to take great care with. In the Yinlips case, these flash chips are glued to the board with a weak glue. But strong enough that the chip won't easily lift once you liquify the solder. So I slide a razor blade under the chip, gently, not pushing the front against the board (which could have cut traces), and only enough to separate the glue on the chip casing from the board a little bit - the razor blade isn't used to cut the pins of the chip from the board... The choice then is, you can use a hot air rework station to remove the chip, or your soldering iron. My hot air rework station was elsewhere and I was in a hurry to try the new flash out. So what I did was to apply flux to the pins on either side of the chip, and then melt enough solder onto the pins on each side, until I got 2 rows of solder that that just covered the space of all of the pins. I started at one side again and kept the solder liquid with the iron until the solder on all of the pads melted an started to give way. Then I gently lifted from underneath the flash with the razor blade, *gently*, until the device separated from the board. When this happened I used a solder sucker to suck up the solder I'd added to that side of the chip. I repeated the process on the other side, and removed the chip. Then I used flux and solder whick to clean up the pads. To solder both of the new 32 gigabyte chips I used the 'drag' method with a hoof tip, and then went over and cleaned up shorts either by dragging away from the chip onto the leads, or using more solder which. A video of the drag method for soldering can be found here:
And the board:
A Micron NAND table entry looks like this in the driver:
The hard part was figuring out the new 'geometry' to use in the table entry. Unfortunately I looked at the datasheets for several supported chips and the values in the tables mostly didn't match the contents of the datasheets. After trial and error, I found out the following - use the page size and the block size specified by the data sheet. Use the chip count (or die count) specified by the datasheet. To figure out the sector count, multiply block * page * die(chip) count, and divide total flash byte capacity by this number. So for the flash I wanted to add, it had 4 dies, 256 blocks, a page size of 8192 - yielding 8388608. Multiply this by 4 and you get 33554432 - the capacity of the chip (remember 1k is 1024 bytes, not 1000). So I used 4 for the sector value.
I decided to unpack the flash .img, modify the individual binaries, repack, and try to flash the new chips. I used 'imgrepacker' to unpack the flash, and the awesome free hex editor from hexedit.com to modify the files - it can open several files at once and allow you to search across all of them for a pattern. I chose an existing Micron table entry, since I had Micron flash. Each table entry points to an 'architecture' structure which has the special commands needed to talk to that branch of chip. These are pretty generic for simple flash commands but unique for the advanced ones, so it's important to reuse a table entry that matches the brand of your flash. I also confirmed from the chip datsheet that the commands for the new chip matched the commands in the architecture structure for the brand.
Revisiting the driver entry, it looked like this:
In hex, that looks like this:
So what I did was to open all of the binaries in the unpacked flash image, and search for this exact pattern. Once I found it, I overwrote the entry with the hex version of the new table entry.
If the new entry were in the code it would look like this:
As hex, it looks like this:
I searched for, and replaced the block (manually by hitting f3) - you can't do a mass hex-based search and replace in the tool. Once I was done, I saved and closed all of the files, and used imgrepacker to re-pack everything into an .img file. Then I used livesuit to flash the device. It flashed it! But it didn't work, it kept rebooting! For shame!
So I needed to debug the issue. On the yinlips boards, there are pads labeled 'rx' and 'tx' - these are used for serial output on the device, to watch the boot process, interact with u-boot, and as a shell when the device is fully booted. These are 3.3v pads, so you need a 3.3v serial TTL cable. I hooked up device ground, rx, and tx, and used a terminal program to watch the boot process. It turned out that part of the bootloader was doing a CRC on one of the files I needed to modify, and refusing to boot from it if it had changed. The assembly looked like this:
So what I did was to figure out the bytecode modification necessary to disable the CRC check on the file - again the CRC didn't match what was hardcoded in the code because I changed a table entry in the file. I specifically changed "check_file" in sprite.axf to return 0 (success in this case) even if the CRC didn't match. To do this I searched for the bytecode pattern for the old assembly, in the same way as I did the table entries above, with the unpacked image, and replaced it everywhere I found it (multiple images in the flash file will contain sprite.axf) with the new bytecode. Then I saved and closed all the files, repacked the image, and re-flashed
This time it worked!
Something LiveSuit does, after it creates all the images hardcoded into the partition config file in the flash image, is to create a partition with whatever is left called 'UDISK' - this partition is used for the 'internal sdcard' partition. So on my 4GB device, I went from a 2GB internal sdcard partition to a 58GB one.
I did this modification on a YDPG18A and a YDPG16 and both worked fine. I imagine this will work on any Allwinner A10 based device.