mirror of
https://github.com/linux-sunxi/sunxi-tools.git
synced 2024-11-23 01:46:44 +08:00
fel: Add SPI flash programmer implementation
Using the new AAPCS function remote execution support, add support to read from and write to SPI flash connected to a device. This allows flashing boot code to a device. Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com> [Andre: adjust to upstream changes] Signed-off-by: Andre Przywara <osp@andrep.de>
This commit is contained in:
parent
1091f3ac6b
commit
3c9bc29f39
3
Makefile
3
Makefile
@ -135,8 +135,9 @@ HOST_CFLAGS = $(DEFAULT_CFLAGS) $(CFLAGS)
|
||||
PROGRESS := progress.c progress.h
|
||||
SOC_INFO := soc_info.c soc_info.h
|
||||
FEL_LIB := fel_lib.c fel_lib.h
|
||||
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
|
||||
|
||||
sunxi-fel: fel.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB)
|
||||
sunxi-fel: fel.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
|
||||
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS)
|
||||
|
||||
|
@ -72,7 +72,7 @@ toolchain = toolchains.find { |toolchain| tool_exists("#{toolchain}gcc") }
|
||||
abort "Can't find any usable ARM crosscompiler.\n" unless toolchain
|
||||
|
||||
# Compile the source file
|
||||
system("#{toolchain}gcc -c -Os -marm -march=armv7-a -mfloat-abi=soft -fstack-usage -fpic -o #{ARGV[0]}.o #{ARGV[0]}")
|
||||
system("#{toolchain}gcc -c -O3 -marm -march=armv7-a -mfloat-abi=soft -fstack-usage -fpic -o #{ARGV[0]}.o #{ARGV[0]}")
|
||||
exit($?.to_i) if $?.to_i != 0
|
||||
|
||||
# Read the stack usage information
|
||||
|
187
fel-remotefunc-spi-data-transfer.c
Normal file
187
fel-remotefunc-spi-data-transfer.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright © 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned char u8;
|
||||
|
||||
#define readl(addr) (*((volatile u32 *)(addr)))
|
||||
#define writel(v, addr) (*((volatile u32 *)(addr)) = (u32)(v))
|
||||
#define readb(addr) (*((volatile u8 *)(addr)))
|
||||
#define writeb(v, addr) (*((volatile u8 *)(addr)) = (u8)(v))
|
||||
|
||||
/*
|
||||
* This is a basic full-duplex SPI data transfer function (we are sending a
|
||||
* block of data and receiving the same amount of data back), doing the job
|
||||
* without any help from DMA. And because we can be running in some rather
|
||||
* adverse conditions (with default PMIC settings, low CPU clock speed and
|
||||
* CPU caches disabled), it is necessary to use 32-bit accesses to read/write
|
||||
* the FIFO buffers. As a result, Allwinner A13 with the default 408MHz CPU
|
||||
* clock speed can successfully handle at least 12 MHz SPI clock speed.
|
||||
*
|
||||
* Supports both sun4i and sun6i variants of the SPI controller (they only
|
||||
* need different hardware register addresses passed as arguments).
|
||||
*/
|
||||
static void inline __attribute((always_inline)) spi_data_transfer(void *buf,
|
||||
u32 bufsize,
|
||||
void *spi_ctl_reg,
|
||||
u32 spi_ctl_xch_bitmask,
|
||||
void *spi_fifo_reg,
|
||||
void *spi_tx_reg,
|
||||
void *spi_rx_reg,
|
||||
void *spi_bc_reg,
|
||||
void *spi_tc_reg,
|
||||
void *spi_bcc_reg)
|
||||
{
|
||||
u32 cnt;
|
||||
u32 rxsize = bufsize;
|
||||
u32 txsize = bufsize;
|
||||
u8 *rxbuf8 = buf;
|
||||
u8 *txbuf8 = buf;
|
||||
u32 *rxbuf;
|
||||
u32 *txbuf;
|
||||
u32 cpsr;
|
||||
|
||||
/* sun6i uses 3 registers, sun4i only needs 2 */
|
||||
writel(bufsize, spi_bc_reg);
|
||||
writel(bufsize, spi_tc_reg);
|
||||
if (spi_bcc_reg)
|
||||
writel(bufsize, spi_bcc_reg);
|
||||
|
||||
/* Fill the TX buffer with some initial data */
|
||||
cnt = (-(u32)txbuf8 & 3) + 60;
|
||||
if (cnt > txsize)
|
||||
cnt = txsize;
|
||||
while (cnt-- > 0) {
|
||||
writeb(*txbuf8++, spi_tx_reg);
|
||||
txsize--;
|
||||
}
|
||||
|
||||
/* Temporarily disable IRQ & FIQ */
|
||||
asm volatile("mrs %0, cpsr" : "=r" (cpsr));
|
||||
asm volatile("msr cpsr_c, %0" :: "r" (cpsr | 0xC0));
|
||||
|
||||
/* Start the data transfer */
|
||||
writel(readl(spi_ctl_reg) | spi_ctl_xch_bitmask, spi_ctl_reg);
|
||||
|
||||
/* Read the initial unaligned part of the data */
|
||||
cnt = (-(u32)rxbuf8 & 3);
|
||||
if (cnt > rxsize)
|
||||
cnt = rxsize;
|
||||
while (cnt > 0) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
if (rxfifo > 0) {
|
||||
*rxbuf8++ = readb(spi_rx_reg);
|
||||
cnt--;
|
||||
rxsize--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fast processing of the aligned part (read/write 32-bit at a time) */
|
||||
rxbuf = (u32 *)rxbuf8;
|
||||
txbuf = (u32 *)txbuf8;
|
||||
while (rxsize >= 4) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
int txfifo = (fiforeg >> 16) & 0x7F;
|
||||
if (rxfifo >= 4) {
|
||||
*rxbuf++ = readl(spi_rx_reg);
|
||||
rxsize -= 4;
|
||||
}
|
||||
if (txfifo < 60 && txsize >= 4) {
|
||||
writel(*txbuf++, spi_tx_reg);
|
||||
txsize -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the trailing part pf the data */
|
||||
rxbuf8 = (u8 *)rxbuf;
|
||||
txbuf8 = (u8 *)txbuf;
|
||||
while (rxsize >= 1) {
|
||||
u32 fiforeg = readl(spi_fifo_reg);
|
||||
int rxfifo = fiforeg & 0x7F;
|
||||
int txfifo = (fiforeg >> 16) & 0x7F;
|
||||
if (rxfifo >= 1) {
|
||||
*rxbuf8++ = readb(spi_rx_reg);
|
||||
rxsize -= 1;
|
||||
}
|
||||
if (txfifo < 60 && txsize >= 1) {
|
||||
writeb(*txbuf8++, spi_tx_reg);
|
||||
txsize -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore CPSR */
|
||||
asm volatile("msr cpsr_c, %0" :: "r" (cpsr));
|
||||
}
|
||||
|
||||
void spi_batch_data_transfer(u8 *buf,
|
||||
void *spi_ctl_reg,
|
||||
u32 spi_ctl_xch_bitmask,
|
||||
void *spi_fifo_reg,
|
||||
void *spi_tx_reg,
|
||||
void *spi_rx_reg,
|
||||
void *spi_bc_reg,
|
||||
void *spi_tc_reg,
|
||||
void *spi_bcc_reg)
|
||||
{
|
||||
u8 wait_for_completion_cmd[2];
|
||||
u8 *backup_buf;
|
||||
u32 bufsize;
|
||||
|
||||
while (1) {
|
||||
u32 code = (buf[0] << 8) | buf[1];
|
||||
|
||||
/* End of data */
|
||||
if (code == 0)
|
||||
return;
|
||||
|
||||
if (code == 0xFFFF) {
|
||||
/* Wait for completion, part 1 */
|
||||
backup_buf = buf;
|
||||
buf = wait_for_completion_cmd;
|
||||
wait_for_completion_cmd[0] = 0x05;
|
||||
bufsize = 2;
|
||||
} else {
|
||||
/* Normal buffer */
|
||||
buf += 2;
|
||||
bufsize = code;
|
||||
}
|
||||
|
||||
spi_data_transfer(buf, bufsize, spi_ctl_reg, spi_ctl_xch_bitmask,
|
||||
spi_fifo_reg, spi_tx_reg, spi_rx_reg,
|
||||
spi_bc_reg, spi_tc_reg, spi_bcc_reg);
|
||||
buf += bufsize;
|
||||
|
||||
if (code == 0xFFFF) {
|
||||
/* Wait for completion, part 2 */
|
||||
buf = backup_buf;
|
||||
if (wait_for_completion_cmd[1] & 1) {
|
||||
/* Still busy */
|
||||
continue;
|
||||
}
|
||||
/* Advance to the next code */
|
||||
buf = backup_buf + 2;
|
||||
}
|
||||
}
|
||||
}
|
150
fel-remotefunc-spi-data-transfer.h
Normal file
150
fel-remotefunc-spi-data-transfer.h
Normal file
@ -0,0 +1,150 @@
|
||||
/* Automatically generated, do not edit! */
|
||||
|
||||
static void
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(feldev_handle *dev,
|
||||
uint32_t buf,
|
||||
uint32_t spi_ctl_reg,
|
||||
uint32_t spi_ctl_xch_bitmask,
|
||||
uint32_t spi_fifo_reg,
|
||||
uint32_t spi_tx_reg,
|
||||
uint32_t spi_rx_reg,
|
||||
uint32_t spi_bc_reg,
|
||||
uint32_t spi_tc_reg,
|
||||
uint32_t spi_bcc_reg)
|
||||
{
|
||||
static uint8_t arm_code[] = {
|
||||
0xf0, 0x0f, 0x2d, 0xe9, /* 0: push {r4, r5, r6, r7, r8, r9, sl, fp} */
|
||||
0x18, 0xd0, 0x4d, 0xe2, /* 4: sub sp, sp, #24 */
|
||||
0x38, 0x50, 0x9d, 0xe5, /* 8: ldr r5, [sp, #56] */
|
||||
0x3c, 0x60, 0x9d, 0xe5, /* c: ldr r6, [sp, #60] */
|
||||
0x06, 0x00, 0x8d, 0xe9, /* 10: stmib sp, {r1, r2} */
|
||||
0x00, 0xa0, 0xd0, 0xe5, /* 14: ldrb sl, [r0] */
|
||||
0x01, 0x20, 0xd0, 0xe5, /* 18: ldrb r2, [r0, #1] */
|
||||
0x0a, 0xa4, 0x92, 0xe1, /* 1c: orrs sl, r2, sl, lsl #8 */
|
||||
0x6a, 0x00, 0x00, 0x0a, /* 20: beq 1d0 <spi_batch_data_transfer+0x1d0> */
|
||||
0xff, 0x2f, 0x0f, 0xe3, /* 24: movw r2, #65535 */
|
||||
0x02, 0x00, 0x5a, 0xe1, /* 28: cmp sl, r2 */
|
||||
0x18, 0x80, 0x8d, 0x02, /* 2c: addeq r8, sp, #24 */
|
||||
0x02, 0x80, 0x80, 0x12, /* 30: addne r8, r0, #2 */
|
||||
0x05, 0xb0, 0xa0, 0x03, /* 34: moveq fp, #5 */
|
||||
0x48, 0xc0, 0x9d, 0xe5, /* 38: ldr ip, [sp, #72] */
|
||||
0x08, 0xb0, 0x68, 0x05, /* 3c: strbeq fp, [r8, #-8]! */
|
||||
0x00, 0x10, 0x68, 0xe2, /* 40: rsb r1, r8, #0 */
|
||||
0x40, 0x20, 0x9d, 0xe5, /* 44: ldr r2, [sp, #64] */
|
||||
0x03, 0x10, 0x01, 0xe2, /* 48: and r1, r1, #3 */
|
||||
0x44, 0xb0, 0x9d, 0xe5, /* 4c: ldr fp, [sp, #68] */
|
||||
0x0a, 0x70, 0xa0, 0x11, /* 50: movne r7, sl */
|
||||
0x0c, 0x00, 0x8d, 0x05, /* 54: streq r0, [sp, #12] */
|
||||
0x3c, 0x00, 0x81, 0xe2, /* 58: add r0, r1, #60 */
|
||||
0x02, 0x70, 0xa0, 0x03, /* 5c: moveq r7, #2 */
|
||||
0x00, 0x00, 0x5c, 0xe3, /* 60: cmp ip, #0 */
|
||||
0x00, 0x70, 0x82, 0xe5, /* 64: str r7, [r2] */
|
||||
0x08, 0x20, 0xa0, 0xe1, /* 68: mov r2, r8 */
|
||||
0x00, 0x70, 0x8b, 0xe5, /* 6c: str r7, [fp] */
|
||||
0x00, 0x70, 0x8c, 0x15, /* 70: strne r7, [ip] */
|
||||
0x07, 0x00, 0x50, 0xe1, /* 74: cmp r0, r7 */
|
||||
0x07, 0x00, 0xa0, 0x21, /* 78: movcs r0, r7 */
|
||||
0x00, 0x40, 0x88, 0xe0, /* 7c: add r4, r8, r0 */
|
||||
0x01, 0xc0, 0xd2, 0xe4, /* 80: ldrb ip, [r2], #1 */
|
||||
0x04, 0x00, 0x52, 0xe1, /* 84: cmp r2, r4 */
|
||||
0x00, 0xc0, 0xc5, 0xe5, /* 88: strb ip, [r5] */
|
||||
0xfb, 0xff, 0xff, 0x1a, /* 8c: bne 80 <spi_batch_data_transfer+0x80> */
|
||||
0x07, 0x00, 0x60, 0xe0, /* 90: rsb r0, r0, r7 */
|
||||
0x00, 0x90, 0x0f, 0xe1, /* 94: mrs r9, CPSR */
|
||||
0xc0, 0xc0, 0x89, 0xe3, /* 98: orr ip, r9, #192 */
|
||||
0x0c, 0xf0, 0x21, 0xe1, /* 9c: msr CPSR_c, ip */
|
||||
0x04, 0xc0, 0x9d, 0xe5, /* a0: ldr ip, [sp, #4] */
|
||||
0x07, 0x00, 0x51, 0xe1, /* a4: cmp r1, r7 */
|
||||
0x07, 0x10, 0xa0, 0x21, /* a8: movcs r1, r7 */
|
||||
0x08, 0xb0, 0x9d, 0xe5, /* ac: ldr fp, [sp, #8] */
|
||||
0x00, 0x40, 0x9c, 0xe5, /* b0: ldr r4, [ip] */
|
||||
0x01, 0xc0, 0x88, 0xe0, /* b4: add ip, r8, r1 */
|
||||
0x00, 0xc0, 0x8d, 0xe5, /* b8: str ip, [sp] */
|
||||
0x08, 0xc0, 0xa0, 0xe1, /* bc: mov ip, r8 */
|
||||
0x0b, 0x40, 0x84, 0xe1, /* c0: orr r4, r4, fp */
|
||||
0x04, 0xb0, 0x9d, 0xe5, /* c4: ldr fp, [sp, #4] */
|
||||
0x00, 0x40, 0x8b, 0xe5, /* c8: str r4, [fp] */
|
||||
0x00, 0xb0, 0x9d, 0xe5, /* cc: ldr fp, [sp] */
|
||||
0x0b, 0x00, 0x5c, 0xe1, /* d0: cmp ip, fp */
|
||||
0x06, 0x00, 0x00, 0x0a, /* d4: beq f4 <spi_batch_data_transfer+0xf4> */
|
||||
0x00, 0x40, 0x93, 0xe5, /* d8: ldr r4, [r3] */
|
||||
0x7f, 0x00, 0x14, 0xe3, /* dc: tst r4, #127 */
|
||||
0xfc, 0xff, 0xff, 0x0a, /* e0: beq d8 <spi_batch_data_transfer+0xd8> */
|
||||
0x00, 0x40, 0xd6, 0xe5, /* e4: ldrb r4, [r6] */
|
||||
0x01, 0x40, 0xcc, 0xe4, /* e8: strb r4, [ip], #1 */
|
||||
0x0b, 0x00, 0x5c, 0xe1, /* ec: cmp ip, fp */
|
||||
0xf8, 0xff, 0xff, 0x1a, /* f0: bne d8 <spi_batch_data_transfer+0xd8> */
|
||||
0x07, 0x10, 0x61, 0xe0, /* f4: rsb r1, r1, r7 */
|
||||
0x03, 0x00, 0x51, 0xe3, /* f8: cmp r1, #3 */
|
||||
0x12, 0x00, 0x00, 0x9a, /* fc: bls 14c <spi_batch_data_transfer+0x14c> */
|
||||
0x00, 0x40, 0x93, 0xe5, /* 100: ldr r4, [r3] */
|
||||
0x7f, 0xb0, 0x04, 0xe2, /* 104: and fp, r4, #127 */
|
||||
0x54, 0x48, 0xe6, 0xe7, /* 108: ubfx r4, r4, #16, #7 */
|
||||
0x03, 0x00, 0x5b, 0xe3, /* 10c: cmp fp, #3 */
|
||||
0x04, 0x10, 0x41, 0xc2, /* 110: subgt r1, r1, #4 */
|
||||
0x00, 0xb0, 0x96, 0xc5, /* 114: ldrgt fp, [r6] */
|
||||
0x04, 0xb0, 0x8c, 0xc4, /* 118: strgt fp, [ip], #4 */
|
||||
0x03, 0x00, 0x50, 0xe3, /* 11c: cmp r0, #3 */
|
||||
0x00, 0xb0, 0xa0, 0x93, /* 120: movls fp, #0 */
|
||||
0x01, 0xb0, 0xa0, 0x83, /* 124: movhi fp, #1 */
|
||||
0x3b, 0x00, 0x54, 0xe3, /* 128: cmp r4, #59 */
|
||||
0x00, 0xb0, 0xa0, 0xc3, /* 12c: movgt fp, #0 */
|
||||
0x00, 0x00, 0x5b, 0xe3, /* 130: cmp fp, #0 */
|
||||
0xef, 0xff, 0xff, 0x0a, /* 134: beq f8 <spi_batch_data_transfer+0xf8> */
|
||||
0x04, 0x40, 0x92, 0xe4, /* 138: ldr r4, [r2], #4 */
|
||||
0x03, 0x00, 0x51, 0xe3, /* 13c: cmp r1, #3 */
|
||||
0x04, 0x00, 0x40, 0xe2, /* 140: sub r0, r0, #4 */
|
||||
0x00, 0x40, 0x85, 0xe5, /* 144: str r4, [r5] */
|
||||
0xec, 0xff, 0xff, 0x8a, /* 148: bhi 100 <spi_batch_data_transfer+0x100> */
|
||||
0x00, 0x00, 0x51, 0xe3, /* 14c: cmp r1, #0 */
|
||||
0x10, 0x00, 0x00, 0x0a, /* 150: beq 198 <spi_batch_data_transfer+0x198> */
|
||||
0x00, 0x40, 0x93, 0xe5, /* 154: ldr r4, [r3] */
|
||||
0x7f, 0x00, 0x14, 0xe3, /* 158: tst r4, #127 */
|
||||
0x54, 0x48, 0xe6, 0xe7, /* 15c: ubfx r4, r4, #16, #7 */
|
||||
0x01, 0x10, 0x41, 0x12, /* 160: subne r1, r1, #1 */
|
||||
0x00, 0xb0, 0xd6, 0x15, /* 164: ldrbne fp, [r6] */
|
||||
0x01, 0xb0, 0xcc, 0x14, /* 168: strbne fp, [ip], #1 */
|
||||
0x00, 0xb0, 0x90, 0xe2, /* 16c: adds fp, r0, #0 */
|
||||
0x01, 0xb0, 0xa0, 0x13, /* 170: movne fp, #1 */
|
||||
0x3b, 0x00, 0x54, 0xe3, /* 174: cmp r4, #59 */
|
||||
0x00, 0xb0, 0xa0, 0xc3, /* 178: movgt fp, #0 */
|
||||
0x00, 0x00, 0x5b, 0xe3, /* 17c: cmp fp, #0 */
|
||||
0xf1, 0xff, 0xff, 0x0a, /* 180: beq 14c <spi_batch_data_transfer+0x14c> */
|
||||
0x01, 0x40, 0xd2, 0xe4, /* 184: ldrb r4, [r2], #1 */
|
||||
0x00, 0x00, 0x51, 0xe3, /* 188: cmp r1, #0 */
|
||||
0x01, 0x00, 0x40, 0xe2, /* 18c: sub r0, r0, #1 */
|
||||
0x00, 0x40, 0xc5, 0xe5, /* 190: strb r4, [r5] */
|
||||
0xee, 0xff, 0xff, 0x1a, /* 194: bne 154 <spi_batch_data_transfer+0x154> */
|
||||
0x09, 0xf0, 0x21, 0xe1, /* 198: msr CPSR_c, r9 */
|
||||
0xff, 0xcf, 0x0f, 0xe3, /* 19c: movw ip, #65535 */
|
||||
0x0c, 0x00, 0x5a, 0xe1, /* 1a0: cmp sl, ip */
|
||||
0x07, 0x00, 0x88, 0x10, /* 1a4: addne r0, r8, r7 */
|
||||
0x99, 0xff, 0xff, 0x1a, /* 1a8: bne 14 <spi_batch_data_transfer+0x14> */
|
||||
0x11, 0x20, 0xdd, 0xe5, /* 1ac: ldrb r2, [sp, #17] */
|
||||
0x01, 0x00, 0x12, 0xe3, /* 1b0: tst r2, #1 */
|
||||
0x08, 0x00, 0x00, 0x1a, /* 1b4: bne 1dc <spi_batch_data_transfer+0x1dc> */
|
||||
0x0c, 0xb0, 0x9d, 0xe5, /* 1b8: ldr fp, [sp, #12] */
|
||||
0x02, 0x00, 0x8b, 0xe2, /* 1bc: add r0, fp, #2 */
|
||||
0x00, 0xa0, 0xd0, 0xe5, /* 1c0: ldrb sl, [r0] */
|
||||
0x01, 0x20, 0xd0, 0xe5, /* 1c4: ldrb r2, [r0, #1] */
|
||||
0x0a, 0xa4, 0x92, 0xe1, /* 1c8: orrs sl, r2, sl, lsl #8 */
|
||||
0x94, 0xff, 0xff, 0x1a, /* 1cc: bne 24 <spi_batch_data_transfer+0x24> */
|
||||
0x18, 0xd0, 0x8d, 0xe2, /* 1d0: add sp, sp, #24 */
|
||||
0xf0, 0x0f, 0xbd, 0xe8, /* 1d4: pop {r4, r5, r6, r7, r8, r9, sl, fp} */
|
||||
0x1e, 0xff, 0x2f, 0xe1, /* 1d8: bx lr */
|
||||
0x0c, 0x00, 0x9d, 0xe5, /* 1dc: ldr r0, [sp, #12] */
|
||||
0x8b, 0xff, 0xff, 0xea, /* 1e0: b 14 <spi_batch_data_transfer+0x14> */
|
||||
};
|
||||
uint32_t args[] = {
|
||||
buf,
|
||||
spi_ctl_reg,
|
||||
spi_ctl_xch_bitmask,
|
||||
spi_fifo_reg,
|
||||
spi_tx_reg,
|
||||
spi_rx_reg,
|
||||
spi_bc_reg,
|
||||
spi_tc_reg,
|
||||
spi_bcc_reg
|
||||
};
|
||||
aw_fel_remotefunc_prepare(dev, 56, arm_code, sizeof(arm_code), 9, args);
|
||||
}
|
463
fel-spiflash.c
Normal file
463
fel-spiflash.c
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fel_lib.h"
|
||||
#include "progress.h"
|
||||
|
||||
#include "fel-remotefunc-spi-data-transfer.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint8_t write_enable_cmd;
|
||||
uint8_t large_erase_cmd;
|
||||
uint32_t large_erase_size;
|
||||
uint8_t small_erase_cmd;
|
||||
uint32_t small_erase_size;
|
||||
uint8_t program_cmd;
|
||||
uint32_t program_size;
|
||||
char *text_description;
|
||||
} spi_flash_info_t;
|
||||
|
||||
spi_flash_info_t spi_flash_info[] = {
|
||||
{ 0xEF40, 0x6, 0xD8, 64 * 1024, 0x20, 4 * 1024, 0x02, 256, "Winbond W25Qxx" },
|
||||
};
|
||||
|
||||
spi_flash_info_t default_spi_flash_info = {
|
||||
0x0000, 0x6, 0xD8, 64 * 1024, 0x20, 4 * 1024, 0x02, 256, "Unknown"
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
uint32_t fel_readl(feldev_handle *dev, uint32_t addr);
|
||||
void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val);
|
||||
#define readl(addr) fel_readl(dev, (addr))
|
||||
#define writel(val, addr) fel_writel(dev, (addr), (val))
|
||||
|
||||
#define PA (0)
|
||||
#define PB (1)
|
||||
#define PC (2)
|
||||
|
||||
#define CCM_SPI0_CLK (0x01C20000 + 0xA0)
|
||||
#define CCM_AHB_GATING0 (0x01C20000 + 0x60)
|
||||
#define CCM_AHB_GATE_SPI0 (1 << 20)
|
||||
#define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0)
|
||||
#define SUN6I_SPI0_RST (1 << 20)
|
||||
|
||||
#define SUNXI_GPC_SPI0 (3)
|
||||
#define SUN50I_GPC_SPI0 (4)
|
||||
|
||||
#define SUN4I_CTL_ENABLE (1 << 0)
|
||||
#define SUN4I_CTL_MASTER (1 << 1)
|
||||
#define SUN4I_CTL_TF_RST (1 << 8)
|
||||
#define SUN4I_CTL_RF_RST (1 << 9)
|
||||
#define SUN4I_CTL_XCH (1 << 10)
|
||||
|
||||
#define SUN6I_TCR_XCH (1 << 31)
|
||||
|
||||
#define SUN4I_SPI0_CCTL (0x01C05000 + 0x1C)
|
||||
#define SUN4I_SPI0_CTL (0x01C05000 + 0x08)
|
||||
#define SUN4I_SPI0_RX (0x01C05000 + 0x00)
|
||||
#define SUN4I_SPI0_TX (0x01C05000 + 0x04)
|
||||
#define SUN4I_SPI0_FIFO_STA (0x01C05000 + 0x28)
|
||||
#define SUN4I_SPI0_BC (0x01C05000 + 0x20)
|
||||
#define SUN4I_SPI0_TC (0x01C05000 + 0x24)
|
||||
|
||||
#define SUN6I_SPI0_CCTL (0x01C68000 + 0x24)
|
||||
#define SUN6I_SPI0_GCR (0x01C68000 + 0x04)
|
||||
#define SUN6I_SPI0_TCR (0x01C68000 + 0x08)
|
||||
#define SUN6I_SPI0_FIFO_STA (0x01C68000 + 0x1C)
|
||||
#define SUN6I_SPI0_MBC (0x01C68000 + 0x30)
|
||||
#define SUN6I_SPI0_MTC (0x01C68000 + 0x34)
|
||||
#define SUN6I_SPI0_BCC (0x01C68000 + 0x38)
|
||||
#define SUN6I_SPI0_TXD (0x01C68000 + 0x200)
|
||||
#define SUN6I_SPI0_RXD (0x01C68000 + 0x300)
|
||||
|
||||
#define CCM_SPI0_CLK_DIV_BY_2 (0x1000)
|
||||
#define CCM_SPI0_CLK_DIV_BY_4 (0x1001)
|
||||
#define CCM_SPI0_CLK_DIV_BY_6 (0x1002)
|
||||
|
||||
/*
|
||||
* Configure pin function on a GPIO port
|
||||
*/
|
||||
static void gpio_set_cfgpin(feldev_handle *dev, int port_num, int pin_num,
|
||||
int val)
|
||||
{
|
||||
uint32_t port_base = 0x01C20800 + port_num * 0x24;
|
||||
uint32_t cfg_reg = port_base + 4 * (pin_num / 8);
|
||||
uint32_t pin_idx = pin_num % 8;
|
||||
uint32_t x = readl(cfg_reg);
|
||||
x &= ~(0x7 << (pin_idx * 4));
|
||||
x |= val << (pin_idx * 4);
|
||||
writel(x, cfg_reg);
|
||||
}
|
||||
|
||||
static bool spi_is_sun6i(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1623: /* A10 */
|
||||
case 0x1625: /* A13 */
|
||||
case 0x1651: /* A20 */
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Init the SPI0 controller and setup pins muxing.
|
||||
*/
|
||||
static bool spi0_init(feldev_handle *dev)
|
||||
{
|
||||
uint32_t reg_val;
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
if (!soc_info)
|
||||
return false;
|
||||
|
||||
/* Setup SPI0 pins muxing */
|
||||
switch (soc_info->soc_id) {
|
||||
case 0x1625: /* Allwinner A13 */
|
||||
case 0x1680: /* Allwinner H3 */
|
||||
case 0x1718: /* Allwinner H5 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUNXI_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUNXI_GPC_SPI0);
|
||||
break;
|
||||
case 0x1689: /* Allwinner A64 */
|
||||
gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 1, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0);
|
||||
gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0);
|
||||
break;
|
||||
default: /* Unknown/Unsupported SoC */
|
||||
return false;
|
||||
}
|
||||
|
||||
reg_val = readl(CCM_AHB_GATING0);
|
||||
reg_val |= CCM_AHB_GATE_SPI0;
|
||||
writel(reg_val, CCM_AHB_GATING0);
|
||||
|
||||
/* 24MHz from OSC24M */
|
||||
writel((1 << 31), CCM_SPI0_CLK);
|
||||
/* divide by 4 */
|
||||
writel(CCM_SPI0_CLK_DIV_BY_4, spi_is_sun6i(dev) ? SUN6I_SPI0_CCTL :
|
||||
SUN4I_SPI0_CCTL);
|
||||
|
||||
if (spi_is_sun6i(dev)) {
|
||||
/* Deassert SPI0 reset */
|
||||
reg_val = readl(SUN6I_BUS_SOFT_RST_REG0);
|
||||
reg_val |= SUN6I_SPI0_RST;
|
||||
writel(reg_val, SUN6I_BUS_SOFT_RST_REG0);
|
||||
/* Enable SPI in the master mode and do a soft reset */
|
||||
reg_val = readl(SUN6I_SPI0_GCR);
|
||||
reg_val |= (1 << 31) | 3;
|
||||
writel(reg_val, SUN6I_SPI0_GCR);
|
||||
/* Wait for completion */
|
||||
while (readl(SUN6I_SPI0_GCR) & (1 << 31)) {}
|
||||
} else {
|
||||
reg_val = readl(SUN4I_SPI0_CTL);
|
||||
reg_val |= SUN4I_CTL_MASTER;
|
||||
reg_val |= SUN4I_CTL_ENABLE | SUN4I_CTL_TF_RST | SUN4I_CTL_RF_RST;
|
||||
writel(reg_val, SUN4I_SPI0_CTL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Backup/restore the initial portion of the SRAM, which can be used as
|
||||
* a temporary data buffer.
|
||||
*/
|
||||
static void *backup_sram(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
void *buf = malloc(bufsize);
|
||||
aw_fel_read(dev, soc_info->spl_addr, buf, bufsize);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void restore_sram(feldev_handle *dev, void *buf)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
aw_fel_write(dev, buf, soc_info->spl_addr, bufsize);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void prepare_spi_batch_data_transfer(feldev_handle *dev, uint32_t buf)
|
||||
{
|
||||
if (spi_is_sun6i(dev)) {
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev,
|
||||
buf,
|
||||
SUN6I_SPI0_TCR,
|
||||
SUN6I_TCR_XCH,
|
||||
SUN6I_SPI0_FIFO_STA,
|
||||
SUN6I_SPI0_TXD,
|
||||
SUN6I_SPI0_RXD,
|
||||
SUN6I_SPI0_MBC,
|
||||
SUN6I_SPI0_MTC,
|
||||
SUN6I_SPI0_BCC);
|
||||
} else {
|
||||
aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev,
|
||||
buf,
|
||||
SUN4I_SPI0_CTL,
|
||||
SUN4I_CTL_XCH,
|
||||
SUN4I_SPI0_FIFO_STA,
|
||||
SUN4I_SPI0_TX,
|
||||
SUN4I_SPI0_RX,
|
||||
SUN4I_SPI0_BC,
|
||||
SUN4I_SPI0_TC,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from the SPI flash. Use the first 4KiB of SRAM as the data buffer.
|
||||
*/
|
||||
void aw_fel_spiflash_read(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
void *backup = backup_sram(dev);
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
if (max_chunk_size > 0x1000)
|
||||
max_chunk_size = 0x1000;
|
||||
uint8_t *cmdbuf = malloc(max_chunk_size);
|
||||
memset(cmdbuf, 0, max_chunk_size);
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, max_chunk_size);
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
|
||||
progress_start(progress, len);
|
||||
while (len > 0) {
|
||||
size_t chunk_size = len;
|
||||
if (chunk_size > max_chunk_size - 8)
|
||||
chunk_size = max_chunk_size - 8;
|
||||
|
||||
memset(cmdbuf, 0, max_chunk_size);
|
||||
cmdbuf[0] = (chunk_size + 4) >> 8;
|
||||
cmdbuf[1] = (chunk_size + 4);
|
||||
cmdbuf[2] = 3;
|
||||
cmdbuf[3] = offset >> 16;
|
||||
cmdbuf[4] = offset >> 8;
|
||||
cmdbuf[5] = offset;
|
||||
|
||||
if (chunk_size == max_chunk_size - 8)
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, 6);
|
||||
else
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, chunk_size + 8);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
aw_fel_read(dev, soc_info->spl_addr + 6, buf8, chunk_size);
|
||||
|
||||
len -= chunk_size;
|
||||
offset += chunk_size;
|
||||
buf8 += chunk_size;
|
||||
progress_update(chunk_size);
|
||||
}
|
||||
|
||||
free(cmdbuf);
|
||||
restore_sram(dev, backup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the SPI flash. Use the first 4KiB of SRAM as the data buffer.
|
||||
*/
|
||||
|
||||
#define CMD_WRITE_ENABLE 0x06
|
||||
|
||||
void aw_fel_spiflash_write_helper(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
size_t erase_size, uint8_t erase_cmd,
|
||||
size_t program_size, uint8_t program_cmd)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr;
|
||||
size_t cmd_idx;
|
||||
|
||||
if (max_chunk_size > 0x1000)
|
||||
max_chunk_size = 0x1000;
|
||||
uint8_t *cmdbuf = malloc(max_chunk_size);
|
||||
cmd_idx = 0;
|
||||
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
|
||||
while (len > 0) {
|
||||
while (len > 0 && max_chunk_size - cmd_idx > program_size + 64) {
|
||||
if (offset % erase_size == 0) {
|
||||
/* Emit write enable command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 1;
|
||||
cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE;
|
||||
/* Emit erase command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 4;
|
||||
cmdbuf[cmd_idx++] = erase_cmd;
|
||||
cmdbuf[cmd_idx++] = offset >> 16;
|
||||
cmdbuf[cmd_idx++] = offset >> 8;
|
||||
cmdbuf[cmd_idx++] = offset;
|
||||
/* Emit wait for completion */
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
}
|
||||
/* Emit write enable command */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 1;
|
||||
cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE;
|
||||
/* Emit page program command */
|
||||
size_t write_count = program_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
cmdbuf[cmd_idx++] = (4 + write_count) >> 8;
|
||||
cmdbuf[cmd_idx++] = 4 + write_count;
|
||||
cmdbuf[cmd_idx++] = program_cmd;
|
||||
cmdbuf[cmd_idx++] = offset >> 16;
|
||||
cmdbuf[cmd_idx++] = offset >> 8;
|
||||
cmdbuf[cmd_idx++] = offset;
|
||||
memcpy(cmdbuf + cmd_idx, buf8, write_count);
|
||||
cmd_idx += write_count;
|
||||
buf8 += write_count;
|
||||
len -= write_count;
|
||||
offset += write_count;
|
||||
/* Emit wait for completion */
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
cmdbuf[cmd_idx++] = 0xFF;
|
||||
}
|
||||
/* Emit the end marker */
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
cmdbuf[cmd_idx++] = 0;
|
||||
|
||||
/* Flush */
|
||||
aw_fel_write(dev, cmdbuf, soc_info->spl_addr, cmd_idx);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
cmd_idx = 0;
|
||||
}
|
||||
|
||||
free(cmdbuf);
|
||||
}
|
||||
|
||||
void aw_fel_spiflash_write(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress)
|
||||
{
|
||||
void *backup = backup_sram(dev);
|
||||
uint8_t *buf8 = (uint8_t *)buf;
|
||||
|
||||
spi_flash_info_t *flash_info = &default_spi_flash_info; /* FIXME */
|
||||
|
||||
if ((offset % flash_info->small_erase_size) != 0) {
|
||||
fprintf(stderr, "aw_fel_spiflash_write: 'addr' must be %d bytes aligned\n",
|
||||
flash_info->small_erase_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
progress_start(progress, len);
|
||||
while (len > 0) {
|
||||
size_t write_count;
|
||||
if ((offset % flash_info->large_erase_size) != 0 ||
|
||||
len < flash_info->large_erase_size) {
|
||||
|
||||
write_count = flash_info->small_erase_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
aw_fel_spiflash_write_helper(dev, offset, buf8,
|
||||
write_count,
|
||||
flash_info->small_erase_size, flash_info->small_erase_cmd,
|
||||
flash_info->program_size, flash_info->program_cmd);
|
||||
} else {
|
||||
write_count = flash_info->large_erase_size;
|
||||
if (write_count > len)
|
||||
write_count = len;
|
||||
aw_fel_spiflash_write_helper(dev, offset, buf8,
|
||||
write_count,
|
||||
flash_info->large_erase_size, flash_info->large_erase_cmd,
|
||||
flash_info->program_size, flash_info->program_cmd);
|
||||
}
|
||||
|
||||
len -= write_count;
|
||||
offset += write_count;
|
||||
buf8 += write_count;
|
||||
progress_update(write_count);
|
||||
}
|
||||
|
||||
restore_sram(dev, backup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the read JEDEC ID (9Fh) command.
|
||||
*/
|
||||
void aw_fel_spiflash_info(feldev_handle *dev)
|
||||
{
|
||||
soc_info_t *soc_info = dev->soc_info;
|
||||
const char *manufacturer;
|
||||
unsigned char buf[] = { 0, 4, 0x9F, 0, 0, 0, 0x0, 0x0 };
|
||||
void *backup = backup_sram(dev);
|
||||
|
||||
if (!spi0_init(dev))
|
||||
return;
|
||||
|
||||
aw_fel_write(dev, buf, soc_info->spl_addr, sizeof(buf));
|
||||
prepare_spi_batch_data_transfer(dev, soc_info->spl_addr);
|
||||
aw_fel_remotefunc_execute(dev, NULL);
|
||||
aw_fel_read(dev, soc_info->spl_addr, buf, sizeof(buf));
|
||||
|
||||
restore_sram(dev, backup);
|
||||
|
||||
/* Assume that the MISO pin is either pulled up or down */
|
||||
if (buf[5] == 0x00 || buf[5] == 0xFF) {
|
||||
printf("No SPI flash detected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (buf[3]) {
|
||||
case 0xEF:
|
||||
manufacturer = "Winbond";
|
||||
break;
|
||||
default:
|
||||
manufacturer = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Manufacturer: %s (%02Xh), model: %02Xh, size: %d bytes.\n",
|
||||
manufacturer, buf[3], buf[4], (1 << buf[5]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Show a help message about the available "spiflash-*" commands.
|
||||
*/
|
||||
void aw_fel_spiflash_help(void)
|
||||
{
|
||||
printf(" spiflash-info Retrieves basic information\n"
|
||||
" spiflash-read addr length file Write SPI flash contents into file\n"
|
||||
" spiflash-write addr file Store file contents into SPI flash\n");
|
||||
}
|
34
fel-spiflash.h
Normal file
34
fel-spiflash.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SUNXI_TOOLS_FEL_SPIFLASH_H
|
||||
#define _SUNXI_TOOLS_FEL_SPIFLASH_H
|
||||
|
||||
#include "fel_lib.h"
|
||||
#include "progress.h"
|
||||
|
||||
void aw_fel_spiflash_read(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress);
|
||||
void aw_fel_spiflash_write(feldev_handle *dev,
|
||||
uint32_t offset, void *buf, size_t len,
|
||||
progress_cb_t progress);
|
||||
void aw_fel_spiflash_info(feldev_handle *dev);
|
||||
void aw_fel_spiflash_help(void);
|
||||
void aw_fel_spi0_init(feldev_handle *dev);
|
||||
|
||||
#endif
|
20
fel.c
20
fel.c
@ -18,6 +18,7 @@
|
||||
#include "common.h"
|
||||
#include "portable_endian.h"
|
||||
#include "fel_lib.h"
|
||||
#include "fel-spiflash.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
@ -1177,6 +1178,8 @@ void usage(const char *cmd) {
|
||||
" clear address length Clear memory\n"
|
||||
" fill address length value Fill memory\n"
|
||||
, cmd);
|
||||
printf("\n");
|
||||
aw_fel_spiflash_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -1347,6 +1350,23 @@ int main(int argc, char **argv)
|
||||
if (!uboot_autostart)
|
||||
printf("Warning: \"uboot\" command failed to detect image! Can't execute U-Boot.\n");
|
||||
skip=2;
|
||||
} else if (strcmp(argv[1], "spiflash-info") == 0) {
|
||||
aw_fel_spiflash_info(handle);
|
||||
} else if (strcmp(argv[1], "spiflash-read") == 0 && argc > 4) {
|
||||
size_t size = strtoul(argv[3], NULL, 0);
|
||||
void *buf = malloc(size);
|
||||
aw_fel_spiflash_read(handle, strtoul(argv[2], NULL, 0), buf, size,
|
||||
pflag_active ? progress_bar : NULL);
|
||||
save_file(argv[4], buf, size);
|
||||
free(buf);
|
||||
skip=4;
|
||||
} else if (strcmp(argv[1], "spiflash-write") == 0 && argc > 3) {
|
||||
size_t size;
|
||||
void *buf = load_file(argv[3], &size);
|
||||
aw_fel_spiflash_write(handle, strtoul(argv[2], NULL, 0), buf, size,
|
||||
pflag_active ? progress_bar : NULL);
|
||||
free(buf);
|
||||
skip=3;
|
||||
} else {
|
||||
pr_fatal("Invalid command %s\n", argv[1]);
|
||||
}
|
||||
|
@ -81,4 +81,12 @@ void fel_clrsetbits_le32(feldev_handle *dev,
|
||||
bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result,
|
||||
bool force_workaround);
|
||||
|
||||
bool aw_fel_remotefunc_prepare(feldev_handle *dev,
|
||||
size_t stack_size,
|
||||
void *arm_code,
|
||||
size_t arm_code_size,
|
||||
size_t num_args,
|
||||
uint32_t *args);
|
||||
bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result);
|
||||
|
||||
#endif /* _SUNXI_TOOLS_FEL_LIB_H */
|
||||
|
Loading…
Reference in New Issue
Block a user