Unverified Commit 6d598a0e authored by Chen-Yu Tsai's avatar Chen-Yu Tsai Committed by GitHub
Browse files

Merge pull request #114 from apritzel/v1.5-rc1

v1.5-rc1
parents 5c197104 585cb1d4
......@@ -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)
......
#!/usr/bin/env ruby
#
# (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/>.
#
if ARGV.size < 2
printf("Usage: #{$PROGRAM_NAME} [c_source_input] [marshalled_header_output]\n\n")
printf("This script uses an ARM toolchain to compile native ARM code, and then\n")
printf("automatically generates the necessary wrapper code for calling it from\n")
printf("the sunxi-fel tool. Executing such compiled pieces of code natively on\n")
printf("the device may be needed for the performance critical parts.\n")
printf("\nExample input file:\n\n")
printf(" unsigned sum(unsigned a, unsigned b)\n")
printf(" {\n")
printf(" return a + b;\n")
printf(" }\n")
printf("\n")
printf("Using this example code inside of sunxi-fel:\n")
printf("\n")
printf(" uint32_t a = 1, b = 2, c;\n")
printf(" aw_fel_remotefunc_prepare_sum(dev, a, b);\n")
printf(" aw_fel_remotefunc_execute(dev, &c);\n")
printf(" printf(\"%%d + %%d = %%d\\n\", a, b, c);\n\n")
printf("If the returned result is not needed (a void function), then the second\n")
printf("argument to the 'aw_fel_remotefunc_execute' function can be NULL.\n\n")
exit(1)
end
def tool_exists(tool_name)
`which #{tool_name} > /dev/null 2>&1`
return $?.to_i == 0
end
def parse_stack_usage(filename)
return unless File.exists?(filename)
File.read(filename).strip.split("\n").map do |l|
if l =~ /\:([^\:\s]+)\s+(\d+)\s+(\S+)/
if $3 != "static"
abort sprintf("Non-static stack usage for function '%s'\n", $1)
end
{function_name: $1, stack_usage: $2.to_i}
else
abort sprintf("Failed to parse stack usage information '%s'\n", l.strip)
end
end
end
toolchains = [
"arm-none-eabi-",
"arm-linux-gnueabihf-",
"arm-none-linux-gnueabi-",
"armv7a-hardfloat-linux-gnueabi-",
]
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 -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
stack_usage = parse_stack_usage("#{ARGV[0]}.su")
if stack_usage.size != 1
abort sprintf("Expected only one function in the source file, but got %s.\n",
stack_usage.map {|a| "'" + a[:function_name] + "()'" }.join(", "))
end
`#{toolchain}size -A #{ARGV[0]}.o`.each_line do |l|
if l =~ /(\S+)\s+(\S+)/
if ($1 == ".data" || $1 == ".bss" || $1 == ".rodata") && $2.to_i > 0
abort "Can't have non-empty '.data', '.bss' or '.rodata' section."
end
end
end
`#{toolchain}objdump -t #{ARGV[0]}.o`.each_line do |l|
if l =~ /\*UND\*/
abort "External references are not allowed: '#{l.strip}'.\n"
end
end
function_name = stack_usage[0][:function_name]
# Read the source file and strip multiline C comments
sourcefile = File.read(ARGV[0]).gsub(/\/\*.*?\*\//m, "")
# Try to find the function and its arguments
unless sourcefile =~ /#{function_name}\((.*?)\)/m
abort sprintf("Can't find the function '%s()' in the source file.\n",
function_name)
end
# Extract the function argument names
function_args = $1.split(",").map {|a| if a.strip =~ /([^\*\s]+)$/ then $1 end }
# Check if there is any return value
have_retval = !(sourcefile =~ /void\s+#{function_name}/m)
###############################################################################
# Generate output file
###############################################################################
out = File.open(ARGV[1], "w")
out.printf("/* Automatically generated, do not edit! */\n\n")
out.printf("static void\n")
funcdecl = sprintf("aw_fel_remotefunc_prepare_#{function_name}(feldev_handle *dev,")
out.printf("%s\n", funcdecl)
out.printf("%s", function_args.map {|a|
" " * funcdecl.index("(") + " uint32_t " + a }.join(",\n"))
out.printf(")\n{\n")
out.printf("\tstatic uint8_t arm_code[] = {\n")
`#{toolchain}objdump -d #{ARGV[0]}.o`.each_line {|l|
next unless l =~ /(\h+)\:\s+(\h+)\s+(\S+)\s+([^;]*)/
addr = $1
opcode = $2
p1 = $3
p2 = $4.strip
opcode = opcode.scan(/../).map {|a| "0x" + a }.reverse.join(", ")
out.printf("\t\t%s, /* %4s: %-8s %-34s \x2a/\n", opcode, addr, p1, p2)
}
out.printf("\t};\n")
out.printf("\tuint32_t args[] = {\n\t\t")
out.printf("%s\n\t};\n", function_args.join(",\n\t\t"))
out.printf("\taw_fel_remotefunc_prepare(dev, %d, arm_code, sizeof(arm_code), %d, args);\n",
stack_usage[0][:stack_usage], function_args.size)
out.printf("}\n")
/*
* 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;
}
}
}
/* 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);
}
/*
* (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[] = {
{ .id = 0xEF40, .write_enable_cmd = 0x6,
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
.program_cmd = 0x02, .program_size = 256,
.text_description = "Winbond W25Qxx" },
{ .id = 0xC220, .write_enable_cmd = 0x6,
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
.program_cmd = 0x02, .program_size = 256,
.text_description = "Macronix MX25Lxxxx" },
};
spi_flash_info_t default_spi_flash_info = {
.id = 0x0000, .write_enable_cmd = 0x6,
.large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024,
.small_erase_cmd = 0x20, .small_erase_size = 4 * 1024,
.program_cmd = 0x02, .program_size = 256,
.text_description = "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;
case 0xC2:
manufacturer = "Macronix";
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");
}
/*
* (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
......@@ -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>
......@@ -243,6 +244,111 @@ void aw_fel_fill(feldev_handle *dev, uint32_t offset, size_t size, unsigned char
}
}
/*
* Upload a function (implemented in native ARM code) to the device and
* prepare for executing it. Use a subset of 32-bit ARM AAPCS calling
* conventions: all arguments are integer 32-bit values, and an optional
* return value is a 32-bit integer too. The function code needs to be
* compiled in the ARM mode (Thumb2 is not supported), it also must be
* a position independent leaf function (have no calls to anything else)
* and have no references to any global variables.
*
* 'stack_size' - the required stack size for the function (can be
* calculated using the '-fstack-usage' GCC option)
* 'arm_code' - a pointer to the memory buffer with the function code
* 'arm_code_size' - the size of the function code
* 'num_args' - the number of 32-bit function arguments
* 'args' - an array with the function argument values
*
* Note: once uploaded, the function can be executed multiple times with
* exactly the same arguments. If some internal state needs to be
* updated between function calls, then it's best to pass a pointer
* to some state structure located elsewhere in SRAM as one of the
* function arguments.
*/
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)
{
size_t idx, i;
size_t tmp_buf_size;
soc_info_t *soc_info = dev->soc_info;
uint32_t *tmp_buf;
uint32_t new_sp, num_args_on_stack = (num_args <= 4 ? 0 : num_args - 4);
uint32_t entry_code[] = {
htole32(0xe58fe040), /* 0: str lr, [pc, #64] */
htole32(0xe58fd040), /* 4: str sp, [pc, #64] */
htole32(0xe59fd040), /* 8: ldr sp, [pc, #64] */
htole32(0xe28fc040), /* c: add ip, pc, #64 */
htole32(0xe1a0200d), /* 10: mov r2, sp */
htole32(0xe49c0004), /* 14: ldr r0, [ip], #4 */
htole32(0xe3500000), /* 18: cmp r0, #0 */
htole32(0x0a000003), /* 1c: beq 30 <entry+0x30> */
htole32(0xe49c1004), /* 20: ldr r1, [ip], #4 */
htole32(0xe4821004), /* 24: str r1, [r2], #4 */
htole32(0xe2500001), /* 28: subs r0, r0, #1 */
htole32(0x1afffffb), /* 2c: bne 20 <entry+0x20> */
htole32(0xe8bc000f), /* 30: ldm ip!, {r0, r1, r2, r3} */
htole32(0xe12fff3c), /* 34: blx ip */
htole32(0xe59fe008), /* 38: ldr lr, [pc, #8] */
htole32(0xe59fd008), /* 3c: ldr sp, [pc, #8] */
htole32(0xe58f0000), /* 40: str r0, [pc] */
htole32(0xe12fff1e), /* 44: bx lr */
htole32(0x00000000), /* 48: .word 0x00000000 */
htole32(0x00000000), /* 4c: .word 0x00000000 */
};
if (!soc_info)
return false;
/* Calculate the stack location */
new_sp = soc_info->scratch_addr +
sizeof(entry_code) +
2 * 4 +
num_args_on_stack * 4 +
4 * 4 +
arm_code_size +
stack_size;
new_sp = (new_sp + 7) & ~7;
tmp_buf_size = new_sp - soc_info->scratch_addr;
tmp_buf = calloc(tmp_buf_size, 1);
memcpy(tmp_buf, entry_code, sizeof(entry_code));
idx = sizeof(entry_code) / 4;
tmp_buf[idx++] = htole32(new_sp);
tmp_buf[idx++] = htole32(num_args_on_stack);
for (i = num_args - num_args_on_stack; i < num_args; i++)
tmp_buf[idx++] = htole32(args[i]);
for (i = 0; i < 4; i++)
tmp_buf[idx++] = (i < num_args ? htole32(args[i]) : 0);
memcpy(tmp_buf + idx, arm_code, arm_code_size);
aw_fel_write(dev, tmp_buf, soc_info->scratch_addr, tmp_buf_size);
free(tmp_buf);
return true;
}
/*
* Execute the previously uploaded function. The 'result' pointer allows to
* retrieve the return value.
*/
bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result)
{
soc_info_t *soc_info = dev->soc_info;
if (!soc_info)
return false;
aw_fel_execute(dev, soc_info->scratch_addr);
if (result) {
aw_fel_read(dev, soc_info->scratch_addr + 0x48, result, sizeof(uint32_t));
*result = le32toh(*result);
}
return true;
}
static uint32_t fel_to_spl_thunk[] = {
#include "thunks/fel-to-spl-thunk.h"
};
......@@ -1072,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);
}
......@@ -1242,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 */
......@@ -310,6 +310,7 @@ show_usage:
switch (argc - optind) {
case 2:
filename[1] = argv[optind+1]; /* out */
/* fall-through */
case 1:
if (strcmp(argv[optind], "-") != 0)
filename[0] = argv[optind]; /* in */
......
......@@ -165,7 +165,7 @@ static void print(const char *buf)
static const char *argv0;
static void usage(int rc )
static __attribute__((noreturn)) void usage(int rc )
{
fputs("sunxi-pio " VERSION "\n\n", stderr);
fprintf(stderr, "usage: %s -m|-i input [-o output] pin..\n", argv0);
......@@ -179,7 +179,7 @@ static void usage(int rc )
fprintf(stderr," Pxx*count Oscillate GPIO output (mmap mode only)\n");
fprintf(stderr," Pxx?pull Configure GPIO input\n");
fprintf(stderr," clean Clean input pins\n");
fprintf(stderr, "\n mode 0-7, 0=input, 1=ouput, 2-7 I/O function\n");
fprintf(stderr, "\n mode 0-7, 0=input, 1=output, 2-7 I/O function\n");
fprintf(stderr, " pull 0=none, 1=up, 2=down\n");
fprintf(stderr, " drive 0-3, I/O drive level\n");
......@@ -427,4 +427,6 @@ int main(int argc, char **argv)
exit(1);
}
}
return 0;
}
......@@ -94,6 +94,22 @@ sram_swap_buffers a80_sram_swap_buffers[] = {
{ .size = 0 } /* End of the table */
};
/*
* H6 has 32KiB of SRAM A at 0x20000 and a large SRAM C at 0x28000. SRAM A
* and SRAM C reside in the address space back-to-back without any gaps, thus
* representing a singe large contiguous area. Everything is the same as on
* A10/A13/A20, but just shifted by 0x20000.
*/
sram_swap_buffers h6_sram_swap_buffers[] = {
/* 0x21C00-0x21FFF (IRQ stack) */
{ .buf1 = 0x21C00, .buf2 = 0x2A400, .size = 0x0400 },
/* 0x25C00-0x26FFF (Stack) */
{ .buf1 = 0x25C00, .buf2 = 0x2A800, .size = 0x1400 },
/* 0x27C00-0x27FFF (Something important) */
{ .buf1 = 0x27C00, .buf2 = 0x2BC00, .size = 0x0400 },
{ .size = 0 } /* End of the table */
};
soc_info_t soc_info_table[] = {
{
.soc_id = 0x1623, /* Allwinner A10 */
......@@ -207,6 +223,18 @@ soc_info_t soc_info_table[] = {
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.sid_base = 0x01C1B000,
.sid_offset = 0x200,
},{
.soc_id = 0x1728, /* Allwinner H6 */
.name = "H6",
.spl_addr = 0x20000,
.scratch_addr = 0x21000,
.thunk_addr = 0x2A200, .thunk_size = 0x200,
.swap_buffers = h6_sram_swap_buffers,
.sid_base = 0x03006000,
.sid_offset = 0x200,
.rvbar_reg = 0x09010040,
/* Check L.NOP in the OpenRISC reset vector */
.needs_smc_workaround_if_zero_word_at_addr = 0x100004,
},{
.swap_buffers = NULL /* End of the table */
}
......
......@@ -61,6 +61,11 @@ typedef unsigned int u32;
#define AW_CCM_BASE 0x01c20000
#define AW_SRAMCTRL_BASE 0x01c00000
#define H6_UART0_BASE 0x05000000
#define H6_PIO_BASE 0x0300B000
#define H6_CCM_BASE 0x03001000
#define H6_SRAMCTRL_BASE 0x03000000
/*****************************************************************************
* GPIO code, borrowed from U-Boot *
*****************************************************************************/
......@@ -140,6 +145,7 @@ enum sunxi_gpio_number {
#define SUN8I_H3_GPA_UART0 (2)
#define SUN8I_V3S_GPB_UART0 (3)
#define SUN50I_H5_GPA_UART0 (2)
#define SUN50I_H6_GPH_UART0 (2)
#define SUN50I_A64_GPB_UART0 (4)
#define SUNXI_GPF_UART0 (4)
......@@ -148,6 +154,8 @@ enum sunxi_gpio_number {
#define SUNXI_GPIO_PULL_UP (1)
#define SUNXI_GPIO_PULL_DOWN (2)
static u32 pio_base;
int sunxi_gpio_set_cfgpin(u32 pin, u32 val)
{
u32 cfg;
......@@ -155,7 +163,7 @@ int sunxi_gpio_set_cfgpin(u32 pin, u32 val)
u32 index = GPIO_CFG_INDEX(pin);
u32 offset = GPIO_CFG_OFFSET(pin);
struct sunxi_gpio *pio =
&((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
&((struct sunxi_gpio_reg *)pio_base)->gpio_bank[bank];
cfg = readl(&pio->cfg[0] + index);
cfg &= ~(0xf << offset);
cfg |= val << offset;
......@@ -170,7 +178,7 @@ int sunxi_gpio_set_pull(u32 pin, u32 val)
u32 index = GPIO_PULL_INDEX(pin);
u32 offset = GPIO_PULL_OFFSET(pin);
struct sunxi_gpio *pio =
&((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
&((struct sunxi_gpio_reg *)pio_base)->gpio_bank[bank];
cfg = readl(&pio->pull[0] + index);
cfg &= ~(0x3 << offset);
cfg |= val << offset;
......@@ -184,7 +192,7 @@ int sunxi_gpio_output(u32 pin, u32 val)
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
struct sunxi_gpio *pio =
&((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
&((struct sunxi_gpio_reg *)pio_base)->gpio_bank[bank];
dat = readl(&pio->dat);
if(val)
dat |= 1 << num;
......@@ -200,7 +208,7 @@ int sunxi_gpio_input(u32 pin)
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
struct sunxi_gpio *pio =
&((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank];
&((struct sunxi_gpio_reg *)pio_base)->gpio_bank[bank];
dat = readl(&pio->dat);
dat >>= num;
return (dat & 0x1);
......@@ -228,9 +236,13 @@ int gpio_direction_output(unsigned gpio, int value)
* *
* Allwinner A10s and A13 are using the same SoC type id, but they can be *
* differentiated using a certain part of the SID register. *
* *
* Allwinner H6 has its memory map totally reworked, but the SRAM controller *
* remains similar; the base of it is moved to 0x03000000. *
*****************************************************************************/
#define VER_REG (AW_SRAMCTRL_BASE + 0x24)
#define H6_VER_REG (H6_SRAMCTRL_BASE + 0x24)
#define SUN4I_SID_BASE 0x01C23800
#define SUN8I_SID_BASE 0x01C14000
......@@ -266,8 +278,21 @@ void soc_detection_init(void)
if (((midr >> 4) & 0xFFF) == 0xC0F) {
soc_id = 0x1639; /* ARM Cortex-A15, so likely Allwinner A80 */
} else {
set_wbit(VER_REG, 1 << 15);
soc_id = readl(VER_REG) >> 16;
u32 reg;
/*
* This register is GICD_IIDR on H6, but unmapped according to
* other known SoCs' user manuals.
*/
reg = readl(0x03021008);
if ((reg & 0xfff) == 0x43b) /* Found GICv2 here, so it's a H6 */
reg = H6_VER_REG;
else
reg = VER_REG;
set_wbit(reg, 1 << 15);
soc_id = readl(reg) >> 16;
}
}
......@@ -279,6 +304,7 @@ void soc_detection_init(void)
#define soc_is_a80() (soc_id == 0x1639)
#define soc_is_a64() (soc_id == 0x1689)
#define soc_is_h5() (soc_id == 0x1718)
#define soc_is_h6() (soc_id == 0x1728)
#define soc_is_r40() (soc_id == 0x1701)
#define soc_is_v3s() (soc_id == 0x1681)
......@@ -334,7 +360,11 @@ int soc_is_h3(void)
#define APB2_GATE_UART_SHIFT (16)
#define APB2_RESET_UART_SHIFT (16)
void clock_init_uart(void)
#define H6_UART_GATE_RESET (H6_CCM_BASE + 0x90C)
#define H6_UART_GATE_SHIFT (0)
#define H6_UART_RESET_SHIFT (16)
void clock_init_uart_legacy(void)
{
/* Open the clock gate for UART0 */
set_wbit(APB2_GATE, 1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
......@@ -342,6 +372,22 @@ void clock_init_uart(void)
set_wbit(APB2_RESET, 1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart_h6(void)
{
/* Open the clock gate for UART0 */
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1));
/* Deassert UART0 reset */
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart(void)
{
if (soc_is_h6())
clock_init_uart_h6();
else
clock_init_uart_legacy();
}
/*****************************************************************************
* UART0 pins muxing is different for different SoC variants. *
* Allwinner A13 is a bit special, because there are no dedicated UART0 pins *
......@@ -382,6 +428,10 @@ void gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN50I_H5_GPA_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN50I_H5_GPA_UART0);
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h6()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
} else if (soc_is_v3s()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0);
......@@ -394,19 +444,21 @@ void gpio_init(void)
/*****************************************************************************/
#define UART0_RBR (SUNXI_UART0_BASE + 0x0) /* receive buffer register */
#define UART0_THR (SUNXI_UART0_BASE + 0x0) /* transmit holding register */
#define UART0_DLL (SUNXI_UART0_BASE + 0x0) /* divisor latch low register */
static u32 uart0_base;
#define UART0_RBR (uart0_base + 0x0) /* receive buffer register */
#define UART0_THR (uart0_base + 0x0) /* transmit holding register */
#define UART0_DLL (uart0_base + 0x0) /* divisor latch low register */
#define UART0_DLH (SUNXI_UART0_BASE + 0x4) /* divisor latch high register */
#define UART0_IER (SUNXI_UART0_BASE + 0x4) /* interrupt enable reigster */
#define UART0_DLH (uart0_base + 0x4) /* divisor latch high register */
#define UART0_IER (uart0_base + 0x4) /* interrupt enable reigster */
#define UART0_IIR (SUNXI_UART0_BASE + 0x8) /* interrupt identity register */
#define UART0_FCR (SUNXI_UART0_BASE + 0x8) /* fifo control register */
#define UART0_IIR (uart0_base + 0x8) /* interrupt identity register */
#define UART0_FCR (uart0_base + 0x8) /* fifo control register */
#define UART0_LCR (SUNXI_UART0_BASE + 0xc) /* line control register */
#define UART0_LCR (uart0_base + 0xc) /* line control register */
#define UART0_LSR (SUNXI_UART0_BASE + 0x14) /* line status register */
#define UART0_LSR (uart0_base + 0x14) /* line status register */
#define BAUD_115200 (0xD) /* 24 * 1000 * 1000 / 16 / 115200 = 13 */
#define NO_PARITY (0)
......@@ -460,6 +512,8 @@ int get_boot_device(void)
u32 *spl_signature = (void *)0x4;
if (soc_is_a64() || soc_is_a80() || soc_is_h5())
spl_signature = (void *)0x10004;
if (soc_is_h6())
spl_signature = (void *)0x20004;
/* Check the eGON.BT0 magic in the SPL header */
if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E)
......@@ -474,9 +528,21 @@ int get_boot_device(void)
return BOOT_DEVICE_UNK;
}
void bases_init(void)
{
if (soc_is_h6()) {
pio_base = H6_PIO_BASE;
uart0_base = H6_UART0_BASE;
} else {
pio_base = SUNXI_PIO_BASE;
uart0_base = SUNXI_UART0_BASE;
}
}
int main(void)
{
soc_detection_init();
bases_init();
gpio_init();
uart0_init();
......@@ -499,6 +565,8 @@ int main(void)
uart0_puts("Allwinner H3!\n");
else if (soc_is_h5())
uart0_puts("Allwinner H5!\n");
else if (soc_is_h6())
uart0_puts("Allwinner H6!\n");
else if (soc_is_r40())
uart0_puts("Allwinner R40!\n");
else if (soc_is_v3s())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment