Commit 2f6e0697 authored by NiteHawk's avatar NiteHawk Committed by GitHub
Browse files

Merge pull request #91 from n1tehawk/20161220_sid-fix

SID fix / H3 workaround
parents 9223a10a 382bb4c2
......@@ -276,23 +276,29 @@ void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val)
fel_writel_n(dev, addr, &val, 1);
}
void aw_fel_print_sid(feldev_handle *dev)
void aw_fel_print_sid(feldev_handle *dev, bool force_workaround)
{
uint32_t key[4];
soc_info_t *soc_info = dev->soc_info;
if (soc_info->sid_addr) {
pr_info("SID key (e-fuses) at 0x%08X\n", soc_info->sid_addr);
uint32_t key[4];
fel_readl_n(dev, soc_info->sid_addr, key, 4);
unsigned int i;
/* output SID in "xxxxxxxx:xxxxxxxx:xxxxxxxx:xxxxxxxx" format */
for (i = 0; i <= 3; i++)
printf("%08x%c", key[i], i < 3 ? ':' : '\n');
} else {
if (!soc_info->sid_base) {
printf("SID registers for your SoC (%s) are unknown or inaccessible.\n",
dev->soc_name);
return;
}
if (soc_info->sid_fix || force_workaround) {
pr_info("Read SID key via registers, base = 0x%08X\n",
soc_info->sid_base);
} else {
pr_info("SID key (e-fuses) at 0x%08X\n",
soc_info->sid_base + soc_info->sid_offset);
}
fel_get_sid_root_key(dev, key, force_workaround);
/* output SID in "xxxxxxxx:xxxxxxxx:xxxxxxxx:xxxxxxxx" format */
for (unsigned i = 0; i <= 3; i++)
printf("%08x%c", key[i], i < 3 ? ':' : '\n');
}
void aw_enable_l2_cache(feldev_handle *dev, soc_info_t *soc_info)
......@@ -1101,7 +1107,9 @@ int main(int argc, char **argv)
} else if (strncmp(argv[1], "ver", 3) == 0) {
aw_fel_print_version(handle);
} else if (strcmp(argv[1], "sid") == 0) {
aw_fel_print_sid(handle);
aw_fel_print_sid(handle, false);
} else if (strcmp(argv[1], "sid-registers") == 0) {
aw_fel_print_sid(handle, true); /* enforce register access */
} else if (strcmp(argv[1], "write") == 0 && argc > 3) {
skip += 2 * file_upload(handle, 1, argc - 2, argv + 2,
pflag_active ? progress_bar : NULL);
......
......@@ -368,6 +368,73 @@ void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count
}
}
/*
* Memory access to the SID (root) keys proved to be unreliable for certain
* SoCs. This function uses an alternative, register-based approach to retrieve
* the values.
*/
static void fel_get_sid_registers(feldev_handle *dev, uint32_t *result)
{
uint32_t arm_code[] = {
htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */
htole32(0xe3a01000), /* 4: mov r1, #0 */
htole32(0xe28f303c), /* 8: add r3, pc, #60 */
/* <sid_read_loop>: */
htole32(0xe1a02801), /* c: lsl r2, r1, #16 */
htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */
htole32(0xe3822002), /* 14: orr r2, r2, #2 */
htole32(0xe5802040), /* 18: str r2, [r0, #64] */
/* <sid_read_wait>: */
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
htole32(0xe3120002), /* 20: tst r2, #2 */
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */
htole32(0xe7832001), /* 2c: str r2, [r3, r1] */
htole32(0xe2811004), /* 30: add r1, r1, #4 */
htole32(0xe3510010), /* 34: cmp r1, #16 */
htole32(0x3afffff3), /* 38: bcc c <sid_read_loop> */
htole32(0xe3a02000), /* 3c: mov r2, #0 */
htole32(0xe5802040), /* 40: str r2, [r0, #64] */
htole32(0xe12fff1e), /* 44: bx lr */
htole32(dev->soc_info->sid_base), /* SID base addr */
/* retrieved SID values go here */
};
/* write and execute code */
aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code));
aw_fel_execute(dev, dev->soc_info->scratch_addr);
/* read back the result */
aw_fel_read(dev, dev->soc_info->scratch_addr + sizeof(arm_code),
result, 4 * sizeof(uint32_t));
for (unsigned i = 0; i < 4; i++)
result[i] = le32toh(result[i]);
}
/* Read the SID "root" key (128 bits). You need to pass the device handle,
* a pointer to a result array capable of receiving at least four 32-bit words,
* and a flag specifying if the register-access workaround should be enforced.
* Return value indicates whether the result is expected to be usable:
* The function will return `false` (and zero the result) if it cannot access
* the SID registers.
*/
bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result,
bool force_workaround)
{
if (!dev->soc_info->sid_base) {
/* SID unavailable */
for (unsigned i = 0; i < 4; i++) result[i] = 0;
return false;
}
if (dev->soc_info->sid_fix || force_workaround)
/* Work around SID issues by using ARM thunk code */
fel_get_sid_registers(dev, result);
else
/* Read SID directly from memory */
fel_readl_n(dev, dev->soc_info->sid_base
+ dev->soc_info->sid_offset, result, 4);
return true;
}
/* general functions, "FEL device" management */
static int feldev_get_endpoint(feldev_handle *dev)
......@@ -601,8 +668,7 @@ feldev_list_entry *list_fel_devices(size_t *count)
strncpy(entry->soc_name, dev->soc_name, sizeof(soc_name_t));
/* retrieve SID bits */
if (dev->soc_info->sid_addr)
aw_fel_readl_n(dev, dev->soc_info->sid_addr, entry->SID, 4);
fel_get_sid_root_key(dev, entry->SID, false);
feldev_close(dev);
free(dev);
......
......@@ -67,4 +67,8 @@ void aw_fel_execute(feldev_handle *dev, uint32_t offset);
void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count);
void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count);
/* retrieve SID root key */
bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result,
bool force_workaround);
#endif /* _SUNXI_TOOLS_FEL_LIB_H */
/*
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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.
*/
/*
* ARM thunk code to read the SID root key using register-based access.
*
* This is necessary for certain SoCs (e.g. H3), as the values read via
* memory mapping might not be consistent. For background information see
* https://groups.google.com/forum/#!topic/linux-sunxi/ynyIP8c61Qs
*/
SID_BASE .req r0
sid_key_index .req r1
.set SID_PRCTL, 0x40 /* SID program/read control register */
.set SID_PRKEY, 0x50 /* SID program key value register */
.set SID_RDKEY, 0x60 /* SID read key value register */
.set SID_OP_LOCK, 0xAC /* Efuse operation lock value */
.set SID_READ_START, (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */
.set SID_PG_START, (1 << 0) /* bit 0 of SID_PRCTL, Software Program Start */
sid_read_root_key:
ldr SID_BASE, sid_base
mov sid_key_index, #0
adr r3, sid_result /* result pointer */
sid_read_loop:
mov r2, sid_key_index, lsl #16 /* PG_INDEX value */
orr r2, #SID_OP_LOCK << 8 /* OP_LOCK to enable SID_READ_START */
orr r2, #SID_READ_START
str r2, [SID_BASE, #SID_PRCTL] /* write SID_PRCTL */
sid_read_wait:
ldr r2, [SID_BASE, #SID_PRCTL] /* read SID_PRCTL */
tst r2, #SID_READ_START /* check if read operation completed */
bne sid_read_wait /* loop while bit 1 still set */
ldr r2, [SID_BASE, #SID_RDKEY] /* read SID key value */
str r2, [r3, sid_key_index] /* store SID value */
add sid_key_index, #4
cmp sid_key_index, #16
blo sid_read_loop /* loop while (sid_key_index < 0x10) */
mov r2, #0
str r2, [SID_BASE, #SID_PRCTL] /* clear SID_PRCTL */
bx lr
sid_base: .word 0
sid_result: .word 0 /* receives the four "root key" 32-bit words */
.word 0
.word 0
.word 0
/* <sid_read_root_key>: */
htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */
htole32(0xe3a01000), /* 4: mov r1, #0 */
htole32(0xe28f303c), /* 8: add r3, pc, #60 */
/* <sid_read_loop>: */
htole32(0xe1a02801), /* c: lsl r2, r1, #16 */
htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */
htole32(0xe3822002), /* 14: orr r2, r2, #2 */
htole32(0xe5802040), /* 18: str r2, [r0, #64] */
/* <sid_read_wait>: */
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
htole32(0xe3120002), /* 20: tst r2, #2 */
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */
htole32(0xe7832001), /* 2c: str r2, [r3, r1] */
htole32(0xe2811004), /* 30: add r1, r1, #4 */
htole32(0xe3510010), /* 34: cmp r1, #16 */
htole32(0x3afffff3), /* 38: bcc c <sid_read_loop> */
htole32(0xe3a02000), /* 3c: mov r2, #0 */
htole32(0xe5802040), /* 40: str r2, [r0, #64] */
htole32(0xe12fff1e), /* 44: bx lr */
/* <sid_base>: */
htole32(0x00000000), /* 48: .word 0x00000000 */
/* <sid_result>: */
......@@ -102,7 +102,7 @@ soc_info_t soc_info_table[] = {
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.needs_l2en = true,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1625, /* Allwinner A10s, A13, R8 */
.name = "A13",
......@@ -110,21 +110,21 @@ soc_info_t soc_info_table[] = {
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.needs_l2en = true,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1651, /* Allwinner A20 */
.name = "A20",
.scratch_addr = 0x1000,
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1650, /* Allwinner A23 */
.name = "A23",
.scratch_addr = 0x1000,
.thunk_addr = 0x46E00, .thunk_size = 0x200,
.swap_buffers = ar100_abusing_sram_swap_buffers,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1633, /* Allwinner A31 */
.name = "A31",
......@@ -137,7 +137,7 @@ soc_info_t soc_info_table[] = {
.scratch_addr = 0x1000,
.thunk_addr = 0x46E00, .thunk_size = 0x200,
.swap_buffers = ar100_abusing_sram_swap_buffers,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1689, /* Allwinner A64 */
.name = "A64",
......@@ -145,7 +145,8 @@ soc_info_t soc_info_table[] = {
.scratch_addr = 0x11000,
.thunk_addr = 0x1A200, .thunk_size = 0x200,
.swap_buffers = a64_sram_swap_buffers,
.sid_addr = 0x01C14200,
.sid_base = 0x01C14000,
.sid_offset = 0x200,
.rvbar_reg = 0x017000A0,
},{
.soc_id = 0x1639, /* Allwinner A80 */
......@@ -154,14 +155,16 @@ soc_info_t soc_info_table[] = {
.scratch_addr = 0x11000,
.thunk_addr = 0x23400, .thunk_size = 0x200,
.swap_buffers = a80_sram_swap_buffers,
.sid_addr = 0x01c0e200,
.sid_base = 0X01C0E000,
.sid_offset = 0x200,
},{
.soc_id = 0x1673, /* Allwinner A83T */
.name = "A83T",
.scratch_addr = 0x1000,
.thunk_addr = 0x46E00, .thunk_size = 0x200,
.swap_buffers = ar100_abusing_sram_swap_buffers,
.sid_addr = 0x01C14200,
.sid_base = 0x01C14000,
.sid_offset = 0x200,
},{
.soc_id = 0x1680, /* Allwinner H3, H2+ */
.name = "H3",
......@@ -169,7 +172,9 @@ soc_info_t soc_info_table[] = {
.mmu_tt_addr = 0x8000,
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.sid_addr = 0x01C14200,
.sid_base = 0x01C14000,
.sid_offset = 0x200,
.sid_fix = true,
},{
.soc_id = 0x1681, /* Allwinner V3s */
.name = "V3s",
......@@ -177,7 +182,7 @@ soc_info_t soc_info_table[] = {
.mmu_tt_addr = 0x8000,
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.sid_addr = 0x01C23800,
.sid_base = 0x01C23800,
},{
.soc_id = 0x1718, /* Allwinner H5 */
.name = "H5",
......@@ -185,7 +190,8 @@ soc_info_t soc_info_table[] = {
.scratch_addr = 0x11000,
.thunk_addr = 0x1A200, .thunk_size = 0x200,
.swap_buffers = a64_sram_swap_buffers,
.sid_addr = 0x01C14200,
.sid_base = 0x01C14000,
.sid_offset = 0x200,
.rvbar_reg = 0x017000A0,
},{
.soc_id = 0x1701, /* Allwinner R40 */
......@@ -193,7 +199,8 @@ soc_info_t soc_info_table[] = {
.scratch_addr = 0x1000,
.thunk_addr = 0xA200, .thunk_size = 0x200,
.swap_buffers = a10_a13_a20_sram_swap_buffers,
.sid_addr = 0x01C1B200,
.sid_base = 0x01C1B000,
.sid_offset = 0x200,
},{
.swap_buffers = NULL /* End of the table */
}
......
......@@ -82,8 +82,10 @@ typedef struct {
uint32_t thunk_size; /* Maximal size of the thunk code */
bool needs_l2en; /* Set the L2EN bit */
uint32_t mmu_tt_addr; /* MMU translation table address */
uint32_t sid_addr; /* base address for SID_KEY[0-3] registers */
uint32_t sid_base; /* base address for SID registers */
uint32_t sid_offset; /* offset for SID_KEY[0-3], "root key" */
uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */
bool sid_fix; /* Use SID workaround (read via register) */
sram_swap_buffers *swap_buffers;
} soc_info_t;
......
......@@ -232,6 +232,29 @@ int gpio_direction_output(unsigned gpio, int value)
#define VER_REG (AW_SRAMCTRL_BASE + 0x24)
#define SUN4I_SID_BASE 0x01C23800
#define SUN8I_SID_BASE 0x01C14000
#define SID_PRCTL 0x40 /* SID program/read control register */
#define SID_RDKEY 0x60 /* SID read key value register */
#define SID_OP_LOCK 0xAC /* Efuse operation lock value */
#define SID_READ_START (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */
u32 sid_read_key(u32 sid_base, u32 offset)
{
u32 reg_val;
reg_val = (offset & 0x1FF) << 16; /* PG_INDEX value */
reg_val |= (SID_OP_LOCK << 8) | SID_READ_START; /* request read access */
writel(reg_val, sid_base + SID_PRCTL);
while (readl(sid_base + SID_PRCTL) & SID_READ_START) ; /* wait while busy */
reg_val = readl(sid_base + SID_RDKEY); /* read SID key value */
writel(0, sid_base + SID_PRCTL); /* clear SID_PRCTL (removing SID_OP_LOCK) */
return reg_val;
}
static u32 soc_id;
......@@ -255,7 +278,6 @@ void soc_detection_init(void)
#define soc_is_a31() (soc_id == 0x1633)
#define soc_is_a80() (soc_id == 0x1639)
#define soc_is_a64() (soc_id == 0x1689)
#define soc_is_h3() (soc_id == 0x1680)
#define soc_is_h5() (soc_id == 0x1718)
#define soc_is_r40() (soc_id == 0x1701)
#define soc_is_v3s() (soc_id == 0x1681)
......@@ -274,6 +296,28 @@ int soc_is_a13(void)
(readl(SUN4I_SID_BASE + 8) & 0xf000) != 0x7000;
}
/* H2+ and H3 share the same ID, we can differentiate them by SID_RKEY0 */
int soc_is_h2_plus(void)
{
if (soc_id != 0x1680) return 0;
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
return (sid0 & 0xff) == 0x42 || (sid0 & 0xff) == 0x83;
}
int soc_is_h3(void)
{
if (soc_id != 0x1680) return 0;
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
/*
* Note: according to Allwinner sources, H3 is expected
* to show up as 0x00, 0x81 or ("H3D") 0x58 here.
*/
return (sid0 & 0xff) != 0x42 && (sid0 & 0xff) != 0x83;
}
/*****************************************************************************
* UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs *
* have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and *
......@@ -330,7 +374,7 @@ void gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h3()) {
} else if (soc_is_h3() || soc_is_h2_plus()) {
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
......@@ -449,6 +493,8 @@ int main(void)
uart0_puts("Allwinner A31/A31s!\n");
else if (soc_is_a64())
uart0_puts("Allwinner A64!\n");
else if (soc_is_h2_plus())
uart0_puts("Allwinner H2+!\n");
else if (soc_is_h3())
uart0_puts("Allwinner H3!\n");
else if (soc_is_h5())
......
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