diff --git a/CLAUDE.md b/CLAUDE.md index c0a5495a7ed7..f37ea7350711 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -101,7 +101,9 @@ The kernel still needs the blob (see "Hand-off to the kernel"). - the words around the table are as in every SDK prebuilt and camera flash seen; - the table is `openipc/.rvs`, taken from the SDK's prebuilt - U-Boots, which matches what is on cameras in the field; + U-Boots, with the eFuse lines as GK7102 cameras in the field have + them (no write to `0x6000B100`; wait for `0x6000B104 == 3` after + starting the load). A GK7102 with the SDK's lines did not start; - GK7101 and GK7102 differ only in the DDR controller word at `0x60004004` (128 vs 64 MiB). `dram_init()` reads the same word back. - **Byte order.** The flash controller packs bytes MSB first. U-Boot, the @@ -117,6 +119,17 @@ The kernel still needs the blob (see "Hand-off to the kernel"). in its `gk7101_sflash.c`), and so does `gk_sflash.c`. `run ub` from either bootloader therefore writes exactly the bytes a programmer would. A CPU-order image (the SDK's prebuilts) is written unchanged. + - `gk_sflash.c` swaps the boot image's span on read as well (the image + found at offset 0 at probe, or the one last written there). Every + caller (`sf read`, SD recovery's "already on the flash" check) sees + the boot image as the file. Everything past it is the controller's + plain round trip. + - A whole-chip dump in chip order (a programmer's) restores with one + `sf write` at 0: a write at 0 that starts with the swapped magic is + swapped over its whole length. `sf update` splits it into sectors and + only the boot image comes out right. `sf read` of the whole chip is + not a dump either: the boot image in chip order, the rest in CPU + order. - Exception: the first release (2026-10-18) wrote CPU-order images and its `gk_sflash.c` has no swap check. `run ub` on a camera running it would write a chip-order image swapped twice and brick it. Upgrade @@ -181,9 +194,17 @@ included. - `gk_ephy_init()` does the vendor's PHY clock (audio PLL, out on a pin), analog trim and MAC/PHY mux. - External-PHY boards (`phytype=1/2`) are not handled. -- **SD:** not enabled. The vendor U-Boot had none, the controller's base - clock is unknown, and the SD pins depend on the board map. SD recovery in - `common/openipc.c` is compiled out with `CONFIG_MMC`. +- **SD:** for recovery, the only way back for a camera without Ethernet + (`common/openipc.c`: `script.txt`, or uImage-wrapped `*.img` written at + their load address). `arch/arm/mach-gk710x/sdhci.c`: + - GK7101/GK7102: a standard SDHCI at `0x60002000`; + - GK710xS: at `0x90000000` in the vendor's halfword layout, so every + access goes through a table of where each SDHCI register and bit + sits, taken from what the S HAL does; + - the base clock comes from the RCT SD PLL and scaler. + + U-Boot sets the map's SD pins and SD power pin. MMC, FAT and LTO + together still fit the 256 KiB boot partition. - **SPI NAND:** not supported. OpenIPC's GK7102 firmware is NOR only. ## The GK710xS (GK7101S, GK7102S, GK7102C) diff --git a/arch/arm/mach-gk710x/Kconfig b/arch/arm/mach-gk710x/Kconfig index dfac3f298e73..e4feead82201 100644 --- a/arch/arm/mach-gk710x/Kconfig +++ b/arch/arm/mach-gk710x/Kconfig @@ -32,6 +32,7 @@ endchoice config GK710X_S bool + select MMC_SDHCI_IO_ACCESSORS if MMC_SDHCI help The GK710xS die: its blocks sit at the addresses the vendor drivers use, in their register layout, instead of being moved and diff --git a/arch/arm/mach-gk710x/Makefile b/arch/arm/mach-gk710x/Makefile index 46654b9be11c..c81b985957b7 100644 --- a/arch/arm/mach-gk710x/Makefile +++ b/arch/arm/mach-gk710x/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += lowlevel_init.o soc.o gpio.o kernel.o +obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/arch/arm/mach-gk710x/include/mach/gk710x.h b/arch/arm/mach-gk710x/include/mach/gk710x.h index d55fd9c4ca24..264ac5ba3684 100644 --- a/arch/arm/mach-gk710x/include/mach/gk710x.h +++ b/arch/arm/mach-gk710x/include/mach/gk710x.h @@ -20,6 +20,7 @@ #ifndef CONFIG_GK710X_S /* AHB */ +#define GK_SDIO0_BASE 0x60002000 /* SDHCI */ #define GK_DDRC_BASE 0x60004000 #define GK_GMAC_BASE 0x6000E000 #define GK_SFLASH_BASE 0x60016000 @@ -52,6 +53,8 @@ #define GK_RCT_DIV_AUDIO_PRE (GK_RCT_BASE + 0x060) #define GK_RCT_SCALER_UART (GK_RCT_BASE + 0x038) #define GK_RCT_PLL_CORE (GK_RCT_BASE + 0x0E4) +#define GK_RCT_PLL_SD (GK_RCT_BASE + 0x000) /* fields moved */ +#define GK_RCT_SCALER_SD (GK_RCT_BASE + 0x00C) #define GK_RCT_IOCTRL_MISC2 (GK_RCT_BASE + 0x200) #define GK_IOCTRL_MISC2_VAL 0x11 /* Pad drive and pull: one byte per pin, four pins per word */ @@ -70,6 +73,7 @@ */ /* AHB */ +#define GK_SDIO0_BASE 0x90000000 /* vendor layout, sdhci.c */ #define GK_DDRC_BASE 0x90002000 #define GK_GMAC_BASE 0x9000E000 /* DWMAC, registers reordered */ #define GK_SFLASH_BASE 0x90016000 @@ -100,6 +104,8 @@ #define GK_RCT_DIV_AUDIO_PST (GK_RCT_BASE + 0x084) #define GK_RCT_DIV_AUDIO_PRE (GK_RCT_BASE + 0x080) #define GK_RCT_SCALER_UART (GK_RCT_BASE + 0x028) +#define GK_RCT_PLL_SD (GK_RCT_BASE + 0x014) +#define GK_RCT_SCALER_SD (GK_RCT_BASE + 0x018) #define GK_RCT_IOCTRL_MISC2 (GK_RCT_BASE + 0x198) #define GK_IOCTRL_MISC2_VAL 0x12 /* the flash HOLD pin pulled up */ /* Pad control words count down from GPIO 55 (gk7101s_gpio.c) */ @@ -149,8 +155,6 @@ #ifndef __ASSEMBLY__ void gk_gpio_config(unsigned int pin, unsigned int type); void gk_gpio_init(void); -/* drivers/mtd/spi/gk_sflash.c, for cmd/sf.c */ -void gk_sflash_boot_order(unsigned int offset, unsigned long len, void *buf); /* board/goke/gk710x/gpio_map.c */ void gk_gpio_map_apply(void); void gk_gpio_map_save(void *dst); diff --git a/arch/arm/mach-gk710x/sdhci.c b/arch/arm/mach-gk710x/sdhci.c new file mode 100644 index 000000000000..60d53504f264 --- /dev/null +++ b/arch/arm/mach-gk710x/sdhci.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Goke GK710x SD controller, for SD-card recovery (common/openipc.c). + * + * Underneath it is a standard SDHCI. On the GK7101/GK7102 it sits at + * 0x60002000 as one. On the GK710xS the vendor drivers reach it with no + * HAL in between, in their own layout (arch-gk7101s/gk7101_reg.h + * REG_SDIO_*): registers in another order, 16 bits wide where SDHCI packs + * bytes, some bits permuted. That layout is exactly what the GK7101/GK7102 + * HAL translates into the SDHCI one, so the table below is that HAL's + * translation, recovered by running its hw_write/hw_read accessors of each + * width under Unicorn (the read and write maps agree), with what the S + * drivers change on top of it: the transfer-mode direction and multi-block + * bits swapped (mach-gk710xs sd.h). The timeout, reset, host, power, gap + * and wake-up registers are bytes with bit orders of their own; the same + * addresses read as words are something else. The accessors put mainline's + * SDHCI driver on top of this table. + */ + +#include +#include +#include +#include +#include +#include + +#define GK_SD_FREQ_MAX 48000000 + +/* + * The SD base clock: 24 MHz * fbdiv / (refdiv * pstdiv1 * pstdiv2) of the + * core PLL, divided by the SD scaler (the vendor kernel's get_sd_freq_hz()). + * The scaler is set here only if nothing has set it. + */ +static unsigned int gk_sd_clock(void) +{ + u32 pll = readl(GK_RCT_PLL_SD); + u32 fbdiv, pst1, pst2, ref, div; + ulong vco; + +#ifdef CONFIG_GK710X_S + fbdiv = pll & 0xfff; + pst1 = (pll >> 12) & 7; + pst2 = (pll >> 16) & 7; + ref = (pll >> 20) & 0x3f; +#else + /* The GK7101/GK7102 HAL moves the fields on the way in */ + fbdiv = (pll >> 8) & 0xfff; + pst1 = (pll >> 20) & 7; + pst2 = (pll >> 24) & 7; + ref = pll & 0x3f; +#endif + if (!fbdiv || !pst1 || !pst2 || !ref) + return GK_SD_FREQ_MAX; + vco = 24000000UL / ref * fbdiv / (pst1 * pst2); + + div = readl(GK_RCT_SCALER_SD); + if (!div) { + div = DIV_ROUND_UP(vco, GK_SD_FREQ_MAX); + writel(div, GK_RCT_SCALER_SD); + } + return vco / div; +} + +#ifdef CONFIG_GK710X_S +/* + * One vendor register: its offset and width, the SDHCI register it is, and + * for each of its bits the SDHCI bit (-1: the bit does not exist there). + * A register not listed is the identity: SDHCI offsets from 0x44 up. + */ +struct gk_sd_reg { + u8 off, width, std; + s8 bit[32]; +}; + +#define ID8 { 0, 1, 2, 3, 4, 5, 6, 7 } +#define ID16 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } +#define ID32 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, \ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 } +#define N -1 + +static const struct gk_sd_reg gk_sd_regs[] = { + { 0x00, 32, SDHCI_DMA_ADDRESS, ID32 }, + { 0x04, 16, SDHCI_TRANSFER_MODE, + { 1, 2, 0, N, 5, 4, N, N, N, N, N, N, N, N, N, N } }, + { 0x06, 16, SDHCI_SIGNAL_ENABLE, ID16 }, + { 0x08, 16, SDHCI_SIGNAL_ENABLE + 2, + { 0, 2, 3, 4, 1, 5, 6, 7, 8, N, N, N, N, N, 14, 15 } }, + { 0x0a, 16, SDHCI_BLOCK_COUNT, ID16 }, + { 0x0c, 16, SDHCI_BLOCK_SIZE, ID16 }, + { 0x0e, 16, SDHCI_INT_ENABLE, ID16 }, + { 0x10, 16, SDHCI_INT_ENABLE + 2, + { 0, 2, 1, 3, 5, 4, 6, 7, 8, N, N, N, N, N, 14, 15 } }, + { 0x12, 16, SDHCI_INT_STATUS, + { 0, 2, 3, 1, 4, 6, 5, 7, 8, 9, N, N, N, N, N, 15 } }, + { 0x14, 16, SDHCI_INT_STATUS + 2, ID16 }, + { 0x16, 16, SDHCI_COMMAND, + { 0, 1, N, 3, 5, 4, 6, 7, 8, 9, 10, 11, 12, 13, N, N } }, + { 0x18, 8, SDHCI_TIMEOUT_CONTROL, ID8 }, + { 0x19, 8, SDHCI_SOFTWARE_RESET, { 1, 0, 2, 3, 4, 5, 6, 7 } }, + { 0x1a, 16, SDHCI_CLOCK_CONTROL, ID16 }, + { 0x1c, 32, SDHCI_RESPONSE, ID32 }, + { 0x20, 32, SDHCI_RESPONSE + 4, ID32 }, + { 0x24, 32, SDHCI_RESPONSE + 8, ID32 }, + { 0x28, 32, SDHCI_RESPONSE + 12, ID32 }, + { 0x2c, 8, SDHCI_HOST_CONTROL, { 0, 1, 3, 2, 4, 5, 6, 7 } }, + { 0x2d, 8, SDHCI_POWER_CONTROL, ID8 }, + { 0x2e, 8, SDHCI_BLOCK_GAP_CONTROL, { 0, 2, 1, 3, 7, 5, 6, 4 } }, + { 0x2f, 8, SDHCI_WAKE_UP_CONTROL, { 1, 0, 2, 3, 4, 5, 6, 7 } }, + { 0x30, 32, SDHCI_PRESENT_STATE, + { 0, 2, 1, N, N, N, N, N, 9, 10, 8, 11, N, N, N, N, + 16, 18, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, N, N, N } }, + { 0x34, 32, SDHCI_ARGUMENT, ID32 }, + { 0x38, 32, SDHCI_CAPABILITIES, + { 0, 1, 2, 3, 4, 5, N, 7, 8, 9, 10, 11, 12, 13, N, N, + 16, 17, 18, N, N, 21, 23, 22, 24, 25, 26, 27, N, N, N, N } }, + { 0x3c, 32, SDHCI_ACMD12_ERR, + { 1, 2, 3, 0, 4, N, N, 7, N, N, N, N, N, N, N, N, + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N } }, + { 0x40, 32, SDHCI_BUFFER, ID32 }, +}; + +static const struct gk_sd_reg *gk_sd_find(int std) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(gk_sd_regs); i++) { + const struct gk_sd_reg *r = &gk_sd_regs[i]; + + if (std >= r->std && std < r->std + r->width / 8) + return r; + } + return NULL; +} + +/* A vendor register's value as SDHCI bits, and back */ +static u32 gk_sd_to_std(const struct gk_sd_reg *r, u32 v) +{ + u32 s = 0; + int i; + + for (i = 0; i < r->width; i++) + if (r->bit[i] >= 0 && (v & BIT(i))) + s |= BIT(r->bit[i]); + return s; +} + +static u32 gk_sd_from_std(const struct gk_sd_reg *r, u32 s) +{ + u32 v = 0; + int i; + + for (i = 0; i < r->width; i++) + if (r->bit[i] >= 0 && (s & BIT(r->bit[i]))) + v |= BIT(i); + return v; +} + +static u32 gk_sd_rd(struct sdhci_host *host, const struct gk_sd_reg *r) +{ + void *a = host->ioaddr + r->off; + + return gk_sd_to_std(r, r->width == 8 ? readb(a) : + r->width == 16 ? readw(a) : readl(a)); +} + +static void gk_sd_wr(struct sdhci_host *host, const struct gk_sd_reg *r, + u32 s) +{ + void *a = host->ioaddr + r->off; + + if (r->width == 8) + writeb(gk_sd_from_std(r, s), a); + else if (r->width == 16) + writew(gk_sd_from_std(r, s), a); + else + writel(gk_sd_from_std(r, s), a); +} + +/* + * An access of `size` bytes at SDHCI offset `reg`: whole vendor registers + * are read or written as they are; a narrower write into one is merged + * with its current value (the control registers SDHCI writes bytewise). + */ +static u32 gk_sd_read(struct sdhci_host *host, int reg, int size) +{ + u32 v = 0; + int i; + + /* No version register in this layout; the vendor clocks it as 2.0 */ + if (reg == SDHCI_HOST_VERSION) + return SDHCI_SPEC_200; + + for (i = 0; i < size; ) { + const struct gk_sd_reg *r = gk_sd_find(reg + i); + int sh; + + if (!r) { + v |= (size == 4 ? readl(host->ioaddr + reg) : + size == 2 ? readw(host->ioaddr + reg) : + readb(host->ioaddr + reg)) << (8 * i); + break; + } + sh = 8 * (reg + i - r->std); + v |= (gk_sd_rd(host, r) >> sh) << (8 * i); + i += r->width / 8 - (reg + i - r->std); + } + return size == 4 ? v : v & ((1u << (8 * size)) - 1); +} + +static void gk_sd_write(struct sdhci_host *host, u32 val, int reg, int size) +{ + int i; + + for (i = 0; i < size; ) { + const struct gk_sd_reg *r = gk_sd_find(reg + i); + int start, n; + u32 mask, s; + + if (!r) { + if (size == 4) + writel(val, host->ioaddr + reg); + else if (size == 2) + writew(val, host->ioaddr + reg); + else + writeb(val, host->ioaddr + reg); + return; + } + start = reg + i - r->std; + n = min(size - i, r->width / 8 - start); + mask = (n == 4 ? ~0u : (1u << (8 * n)) - 1) << (8 * start); + s = (val >> (8 * i)) << (8 * start) & mask; + if (n * 8 != r->width) + s |= gk_sd_rd(host, r) & ~mask; + gk_sd_wr(host, r, s); + i += n; + } +} + +static u32 gk_sd_readl(struct sdhci_host *host, int reg) +{ + return gk_sd_read(host, reg, 4); +} + +static u16 gk_sd_readw(struct sdhci_host *host, int reg) +{ + return gk_sd_read(host, reg, 2); +} + +static u8 gk_sd_readb(struct sdhci_host *host, int reg) +{ + return gk_sd_read(host, reg, 1); +} + +static void gk_sd_writel(struct sdhci_host *host, u32 val, int reg) +{ + gk_sd_write(host, val, reg, 4); +} + +static void gk_sd_writew(struct sdhci_host *host, u16 val, int reg) +{ + gk_sd_write(host, val, reg, 2); +} + +static void gk_sd_writeb(struct sdhci_host *host, u8 val, int reg) +{ + gk_sd_write(host, val, reg, 1); +} + +static const struct sdhci_ops gk_sd_ops = { + .read_l = gk_sd_readl, + .read_w = gk_sd_readw, + .read_b = gk_sd_readb, + .write_l = gk_sd_writel, + .write_w = gk_sd_writew, + .write_b = gk_sd_writeb, +}; +#endif /* CONFIG_GK710X_S */ + +int board_mmc_init(struct bd_info *bis) +{ + struct sdhci_host *host = calloc(1, sizeof(*host)); + unsigned int clk = gk_sd_clock(); + + if (!host) + return -ENOMEM; + host->name = "gk-sdhci"; + host->ioaddr = (void *)GK_SDIO0_BASE; + host->max_clk = clk; + host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + /* The vendor layout has no power register: the slot is always on */ + host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_NO_HISPD_BIT; +#ifdef CONFIG_GK710X_S + host->ops = &gk_sd_ops; +#endif + return add_sdhci(host, clk, 400000); +} diff --git a/board/goke/gk710x/gpio_map.c b/board/goke/gk710x/gpio_map.c index 7af2d1ad0f21..7ce1d171d8f9 100644 --- a/board/goke/gk710x/gpio_map.c +++ b/board/goke/gk710x/gpio_map.c @@ -510,15 +510,35 @@ static const struct gk_gpio_map *gk_map(void) } /* - * U-Boot itself needs one pin: the internal PHY's reference clock, out on - * GPIO 48 in both layouts. The vendor U-Boot applied the whole map here, - * but the map is a guess until gpiomap is set, and the v2 map moves UART0 - * off GPIO 30/31 -- a v1 board would lose its console before anyone could - * set gpiomap=v1. The pins stay as the boot ROM left them; the kernel - * applies the full map it is handed. + * U-Boot itself needs the internal PHY's reference clock, out on GPIO 48 + * in the layouts that have it, and the SD pins where the map is certain. The vendor U-Boot applied + * the whole map here, but the map is a guess until gpiomap is set, and the + * v2 map moves UART0 off GPIO 30/31 -- a v1 board would lose its console + * before anyone could set gpiomap=v1. The other pins stay as the boot ROM + * left them; the kernel applies the full map it is handed. */ #define GK_EPHY_CLK_PIN 48 +/* + * The SD pin functions: card detect, CMD, CLK, DATA0-3. Their pin types + * are the same on both dies. With the map's SD power pin they are what + * U-Boot needs for SD-card recovery; on boards without SD the map has + * none of them. + */ +static bool gk_gpio_is_sd(u32 type) +{ + static const u32 sd[] = { + 0x50010f00, 0xd090181e, 0x9000001f, + 0xd0881016, 0xd0891117, 0xd08a1218, 0xd08b1319, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(sd); i++) + if (type == sd[i]) + return true; + return false; +} + void gk_gpio_map_apply(void) { const struct gk_gpio_map *m = gk_map(); @@ -527,6 +547,18 @@ void gk_gpio_map_apply(void) for (i = 0; i < m->nintphy; i++) if (m->intphy[i].pin == GK_EPHY_CLK_PIN) gk_gpio_config(m->intphy[i].pin, m->intphy[i].type); + /* + * The SD pins only from a map known to be the board's: the GK710xS + * has one, the GK7101/GK7102 default is a guess until gpiomap is set, + * and the v2 SD pins are a v1 board's PHY reset, audio clock and PHY + * data lines (v1 boards have no SD). + */ + if (!IS_ENABLED(CONFIG_GK710X_S) && !env_get("gpiomap")) + m = NULL; + for (i = 0; m && i < m->npins; i++) + if (gk_gpio_is_sd(m->pins[i].type) || + m->pins[i].pin == m->sd_power) + gk_gpio_config(m->pins[i].pin, m->pins[i].type); gk_gpio_init(); } diff --git a/cmd/sf.c b/cmd/sf.c index 5b4e7a3f7632..bd102f5af9df 100644 --- a/cmd/sf.c +++ b/cmd/sf.c @@ -16,9 +16,6 @@ #include #include #include -#ifdef CONFIG_GK_SFLASH -#include -#endif #include #include @@ -309,12 +306,6 @@ static int do_spi_flash_read_write(int argc, char *const argv[]) return 1; } -#ifdef CONFIG_GK_SFLASH - /* A Goke boot image in the flash's byte order goes in swapped back */ - if (strcmp(argv[0], "update") == 0 || strncmp(argv[0], "write", 5) == 0) - gk_sflash_boot_order(offset, len, buf); -#endif - if (strcmp(argv[0], "update") == 0) { ret = spi_flash_update(flash, offset, len, buf); } else if (strncmp(argv[0], "read", 4) == 0 || diff --git a/common/openipc.c b/common/openipc.c index 00c363de3dbf..c9df50841b35 100644 --- a/common/openipc.c +++ b/common/openipc.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #ifdef CONFIG_USB #include #endif @@ -20,7 +22,7 @@ #ifdef CONFIG_ENV_IS_IN_NAND #include #endif -#ifdef CONFIG_ENV_IS_IN_NAND +#ifdef CONFIG_FMC_SPI_NAND #include #include #include @@ -29,8 +31,9 @@ #include #endif -#define SCAN_FROM CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE +#define SCAN_FROM (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE) #define SCAN_STEP 0x10000 +/* How far to look for the kernel, and for the rootfs past the kernel's end */ #define SCAN_LIMIT 0x400000 #define UBI_MAGIC_ASCII "UBI#" @@ -40,14 +43,14 @@ #define SQSH_MAGIC_BE 0x73717368 #define SQSH_MAGIC_LE 0x68737173 -static int check_squashfs(void *buf) { +static int __maybe_unused check_squashfs(void *buf) { uint32_t magic = get_unaligned_be32(buf); if (magic == SQSH_MAGIC_BE || magic == SQSH_MAGIC_LE) return 1; return 0; } -#ifdef CONFIG_ENV_IS_IN_NAND +#ifdef CONFIG_FMC_SPI_NAND #define UBIFS_SB_MAGIC 0x06101831 static int check_ubifs(void *buf) { @@ -76,7 +79,7 @@ static int ubi_marker_present(void) { } #endif -static int check_kernel(void *buf) { +static int __maybe_unused check_kernel(void *buf) { struct image_header *hdr = (struct image_header *)buf; if (image_check_magic(hdr)) { return 1; @@ -91,7 +94,7 @@ static int check_kernel(void *buf) { return 0; } -#ifndef CONFIG_ENV_IS_IN_NAND +#ifndef CONFIG_FMC_SPI_NAND static uint64_t get_squashfs_size(void *buf) { uint32_t magic = get_unaligned_be32(buf); @@ -118,10 +121,19 @@ static int check_rootfs(void *buf, uint64_t *rootfs_size) { return 0; } +/* The kernel image's size from its header, or 0 when it has none */ +static ulong kernel_image_size(void *buf) { + if (image_check_magic((struct image_header *)buf)) + return image_get_image_size((struct image_header *)buf); + if (fdt_magic(buf) == FDT_MAGIC && fdt_check_header(buf) == 0) + return fdt_totalsize(buf); + return 0; +} + static int scan_spi_device(struct spi_flash *flash, ulong *kernel_off, ulong *rootfs_off, uint64_t *kernel_size, uint64_t *rootfs_size) { u8 *buf; - ulong offset; + ulong offset, limit; ulong erase_size; int ret; @@ -135,9 +147,10 @@ static int scan_spi_device(struct spi_flash *flash, ulong *kernel_off, ulong *ro erase_size = 0x10000; #endif - printf("Scanning 0x%x-0x%x ...\n", SCAN_FROM, SCAN_LIMIT); + limit = min_t(ulong, SCAN_FROM + SCAN_LIMIT, flash->size); + printf("Scanning 0x%x-0x%lx ...\n", SCAN_FROM, limit); - for (offset = SCAN_FROM; offset < SCAN_LIMIT && offset < flash->size; offset += SCAN_STEP) { + for (offset = SCAN_FROM; offset < limit; offset += SCAN_STEP) { ret = spi_flash_read(flash, offset, SCAN_STEP, buf); if (ret != 0) continue; @@ -156,8 +169,20 @@ static int scan_spi_device(struct spi_flash *flash, ulong *kernel_off, ulong *ro } if (!*kernel_off && check_kernel(buf)) { + ulong end = offset + ALIGN(kernel_image_size(buf), SCAN_STEP); + *kernel_off = offset; printf("Found Kernel at 0x%08lx\n", offset); + /* + * The rootfs is past the kernel: skip the image, whose contents could + * pass for one, and look as far past its end as before it. A large + * kernel (arm64) ends beyond the first window. + */ + if (end > offset) { + limit = min_t(ulong, max_t(ulong, limit, end + SCAN_LIMIT), flash->size); + offset = end - SCAN_STEP; + continue; + } } if (!*rootfs_off && check_rootfs(buf, rootfs_size)) { @@ -183,21 +208,53 @@ static int scan_spi_device(struct spi_flash *flash, ulong *kernel_off, ulong *ro return 0; } -#endif /* !CONFIG_ENV_IS_IN_NAND */ +#endif /* !CONFIG_FMC_SPI_NAND */ #if (defined(CONFIG_MMC) || defined(CONFIG_USB_STORAGE)) && defined(CONFIG_FS_FAT) #define RECOVERY_KERNEL 0 #define RECOVERY_ROOTFS 1 +static const char *recovery_if, *recovery_part; + +/* The first partition, else the whole medium (a card formatted without a + * partition table); the one that worked is remembered for the calls after. */ static int recovery_mount(const char *ifname) { - if (!fs_set_blk_dev(ifname, "0:1", FS_TYPE_FAT)) + if (recovery_if != ifname) { + recovery_if = ifname; + recovery_part = NULL; + } + if (recovery_part) + return fs_set_blk_dev(ifname, recovery_part, FS_TYPE_FAT) ? -1 : 0; + if (!fs_set_blk_dev(ifname, "0:1", FS_TYPE_FAT)) { + recovery_part = "0:1"; return 0; - if (!fs_set_blk_dev(ifname, "0", FS_TYPE_FAT)) + } + if (!fs_set_blk_dev(ifname, "0", FS_TYPE_FAT)) { + recovery_part = "0"; return 0; + } return -1; } -#ifdef CONFIG_ENV_IS_IN_NAND +/* + * Every fs_* call ends in fs_close(), which forgets the device: the next one + * would read through no filesystem at all ("failed to read" right after + * fs_exists found the file). So each call selects the medium again. + */ +static int rfs_exists(const char *name) { + return !recovery_mount(recovery_if) && fs_exists(name); +} + +static int rfs_size(const char *name, loff_t *size) { + return recovery_mount(recovery_if) ? -1 : fs_size(name, size); +} + +static int rfs_read(const char *name, ulong addr, loff_t offset, loff_t len, + loff_t *actread) { + return recovery_mount(recovery_if) ? -1 : fs_read(name, addr, offset, len, actread); +} + +#ifdef CONFIG_FMC_SPI_NAND static int recovery_ensure_volume(const char *name, const char *size) { struct ubi_volume_desc *desc; char cmd[64]; @@ -252,14 +309,14 @@ static int recovery_write(const char *name, int type) { ulong size; int ret; - if (!fs_exists(name)) + if (!rfs_exists(name)) return 0; if (type == RECOVERY_ROOTFS) { /* Raw ubifs image, written into the rootfs volume */ loff_t fsize; - ret = fs_size(name, &fsize); + ret = rfs_size(name, &fsize); if (ret || fsize == 0) { printf("Recovery: failed to stat %s\n", name); return -1; @@ -267,7 +324,7 @@ static int recovery_write(const char *name, int type) { size = fsize; } else { /* Kernel: uImage or FIT, written to the kernel volume */ - ret = fs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread); + ret = rfs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread); if (ret || actread != sizeof(hdr)) { printf("Recovery: failed to read %s\n", name); return -1; @@ -283,7 +340,7 @@ static int recovery_write(const char *name, int type) { } } - ret = fs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); + ret = rfs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); if (ret || actread != size) { printf("Recovery: failed to read %s\n", name); return -1; @@ -338,50 +395,138 @@ static int recovery_write(const char *name, int type) { #else static struct spi_flash *recovery_flash; -static int recovery_write(const char *name, int type) { - struct image_header hdr; - loff_t actread; - ulong addr, size, erase_len; - int ret; +static int recovery_flash_holds(ulong addr, const void *data, ulong len) { + const ulong chunk = 0x10000; + u8 *buf = malloc(chunk); + ulong off, n; + int same = 1; - if (!fs_exists(name)) + if (!buf) return 0; - - ret = fs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread); - if (ret || actread != sizeof(hdr)) { - printf("Recovery: failed to read %s\n", name); - return -1; + for (off = 0; same && off < len; off += n) { + n = min(len - off, chunk); + same = !spi_flash_read(recovery_flash, addr + off, n, buf) && + !memcmp(buf, (const u8 *)data + off, n); } + free(buf); + return same; +} - if (image_check_magic(&hdr)) { - addr = image_get_load(&hdr); - size = sizeof(hdr) + image_get_data_size(&hdr); - } else if (type == RECOVERY_KERNEL && fdt_magic(&hdr) == FDT_MAGIC && - fdt_check_header(&hdr) == 0) { - const char *str = env_get("kernaddr"); +/* + * One recovery image: a uImage wrapper whose load address is where in the + * flash its payload goes. Whatever the payload is -- a kernel, a rootfs, a + * whole firmware, U-Boot itself -- it is written there; the wrapper header + * never is. Returns 1 when it wrote, 0 when it skipped, -1 on a failed write. + */ +/* What a file read to CONFIG_SYS_LOAD_ADDR may take: the RAM below the + * stack and the relocated U-Boot, with room */ +static ulong recovery_ram_room(void) { + return gd->start_addr_sp - SZ_1M - CONFIG_SYS_LOAD_ADDR; +} - addr = str ? hextoul(str, NULL) : OPENIPC_KERNADDR; - size = fdt_totalsize(&hdr); - } else { - printf("Recovery: no valid uImage/FIT header in %s\n", name); - return -1; +static int recovery_write_img(const char *name, loff_t size) { + struct image_header hdr; + loff_t actread; + ulong addr, len, sect = recovery_flash->sector_size; + + if (size <= (loff_t)sizeof(hdr) || + rfs_read(name, (ulong)&hdr, 0, sizeof(hdr), &actread) || + actread != sizeof(hdr) || !image_check_magic(&hdr) || + !image_check_hcrc(&hdr)) { + printf("Recovery: %s is not a uImage, skipped\n", name); + return 0; + } + addr = image_get_load(&hdr); + len = image_get_data_size(&hdr); + /* a read of length 0 would be a read of the whole file */ + if (!len) { + printf("Recovery: %s has no data, skipped\n", name); + return 0; + } + /* in 64 bits: a size near 4 GiB would wrap a 32-bit sum */ + if ((loff_t)len > size - (loff_t)sizeof(hdr)) { + printf("Recovery: %s is shorter than its header says, skipped\n", name); + return 0; + } + if (addr >= recovery_flash->size || len > recovery_flash->size - addr) { + printf("Recovery: %s (0x%lx + %lu bytes) is outside the flash, skipped\n", + name, addr, len); + return 0; + } + if (addr % sect) { + printf("Recovery: %s: 0x%lx is not on a %lu-byte erase boundary, skipped\n", + name, addr, sect); + return 0; + } + if (len > recovery_ram_room()) { + printf("Recovery: %s (%lu bytes) does not fit the free RAM, skipped\n", + name, len); + return 0; + } + if (rfs_read(name, CONFIG_SYS_LOAD_ADDR, sizeof(hdr), len, &actread) || + actread != len) { + printf("Recovery: failed to read %s, skipped\n", name); + return 0; + } + if (crc32(0, (const u8 *)CONFIG_SYS_LOAD_ADDR, len) != image_get_dcrc(&hdr)) { + printf("Recovery: %s: the data CRC does not match, skipped\n", name); + return 0; + } + /* A card left in the slot: nothing to do, the system boots */ + if (recovery_flash_holds(addr, (void *)CONFIG_SYS_LOAD_ADDR, len)) { + printf("Recovery: %s is already on the flash at 0x%lx\n", name, addr); + return 0; } - ret = fs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); - if (ret || actread != size) { - printf("Recovery: failed to read %s\n", name); + printf("Recovery: writing %s (%lu bytes) at 0x%lx\n", name, len, addr); + if (spi_flash_erase(recovery_flash, addr, ALIGN(len, sect)) || + spi_flash_write(recovery_flash, addr, len, (void *)CONFIG_SYS_LOAD_ADDR)) { + printf("Recovery: writing %s failed\n", name); return -1; } + printf("Recovery: wrote %s\n", name); + return 1; +} - erase_len = (size + recovery_flash->sector_size - 1) & - ~(recovery_flash->sector_size - 1); - spi_flash_erase(recovery_flash, addr, erase_len); - spi_flash_write(recovery_flash, addr, size, (void *)CONFIG_SYS_LOAD_ADDR); - - printf("Recovery: wrote %s (%lu bytes) to flash at 0x%lx\n", name, size, - addr); +struct recovery_file { + char name[256]; + loff_t size; +}; + +/* Every *.img in the root of the medium, in the order the directory lists + * them. The names are collected first: reading a file closes the + * filesystem under an open directory stream. */ +static int recovery_images(void) { + struct recovery_file *files = NULL, *more; + struct fs_dir_stream *dirs; + struct fs_dirent *d; + int n = 0, i, written = 0; + size_t l; + + if (recovery_mount(recovery_if)) + return 0; + dirs = fs_opendir("/"); + if (!dirs) + return 0; + while ((d = fs_readdir(dirs))) { + l = strlen(d->name); + if (d->type != FS_DT_REG || l < 5 || l >= sizeof(files->name) || + strcasecmp(d->name + l - 4, ".img")) + continue; + more = realloc(files, (n + 1) * sizeof(*files)); + if (!more) + break; + files = more; + strcpy(files[n].name, d->name); + files[n++].size = d->size; + } + fs_closedir(dirs); - return 1; + for (i = 0; i < n; i++) + if (recovery_write_img(files[i].name, files[i].size) > 0) + written++; + free(files); + return written; } #endif @@ -392,16 +537,20 @@ static int recovery_run_script(const char *name) { int ret; char cmd[32]; - if (!fs_exists(name)) + if (!rfs_exists(name)) return 0; - ret = fs_size(name, &size); + ret = rfs_size(name, &size); if (ret || size == 0) { printf("Recovery: failed to stat %s\n", name); return 1; } + if ((u64)size > recovery_ram_room()) { + printf("Recovery: %s (%lld bytes) does not fit the free RAM\n", name, size); + return 1; + } - ret = fs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); + ret = rfs_read(name, CONFIG_SYS_LOAD_ADDR, 0, size, &actread); if (ret || actread != size) { printf("Recovery: failed to read %s\n", name); return 1; @@ -416,51 +565,37 @@ static int recovery_run_script(const char *name) { ret = run_command_list((char *)CONFIG_SYS_LOAD_ADDR, size, 0); } - printf("Recovery: executed %s (%lld bytes)\n", name, size); + /* The script took recovery over: its result is reported, not retried + * with the images */ + printf("Recovery: executed %s (%lld bytes)%s\n", name, size, + ret ? ", it failed" : ""); return 1; } static int recovery_try(void) { +#ifdef CONFIG_FMC_SPI_NAND char name[64]; - int ret, found = 0; +#endif - /* script.txt takes precedence over the kernel/rootfs search */ + /* script.txt takes precedence over the image search */ if (recovery_run_script("script.txt")) return 1; -#ifndef CONFIG_ENV_IS_IN_NAND - /* Kernel: uImage preferred, FIT fallback */ - sprintf(name, "uImage.%s.img", CONFIG_PRODUCT_SOC); - ret = recovery_write(name, RECOVERY_KERNEL); - if (ret > 0) { - found = 1; - } else if (ret == 0) { - sprintf(name, "fitImage.%s.img", CONFIG_PRODUCT_SOC); - ret = recovery_write(name, RECOVERY_KERNEL); - if (ret > 0) - found = 1; - } -#endif - +#ifdef CONFIG_FMC_SPI_NAND /* Rootfs. On NAND it carries the kernel (/boot), its volume is sized * to the image and rootfs_data takes the rest. */ -#ifdef CONFIG_ENV_IS_IN_NAND sprintf(name, "rootfs.ubifs.%s.img", CONFIG_PRODUCT_SOC); + return recovery_write(name, RECOVERY_ROOTFS) > 0; #else - sprintf(name, "rootfs.squashfs.%s.img", CONFIG_PRODUCT_SOC); + return recovery_images() > 0; #endif - ret = recovery_write(name, RECOVERY_ROOTFS); - if (ret > 0) - found = 1; - - return found; } int firmware_recovery(void) { int found = 0; -#ifndef CONFIG_ENV_IS_IN_NAND +#ifndef CONFIG_FMC_SPI_NAND recovery_flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, CONFIG_SF_DEFAULT_CS, CONFIG_SF_DEFAULT_SPEED, CONFIG_SF_DEFAULT_MODE); @@ -483,7 +618,7 @@ int firmware_recovery(void) { #endif fs_close(); -#ifndef CONFIG_ENV_IS_IN_NAND +#ifndef CONFIG_FMC_SPI_NAND spi_flash_free(recovery_flash); #endif @@ -495,8 +630,77 @@ int firmware_recovery(void) { } #endif +#if !defined(CONFIG_FMC_SPI_NAND) && defined(CONFIG_OPENIPC_NOR_SPLIT) +/* + * The split layout needs nothing from bootargs: the kernel's device tree has + * the partitions and the kernel takes the root its firmware split finds. A + * saved environment still spells out the fixed slots (mtdparts=..., root= + * /dev/mtdblock3), and an older build of this bootloader added + * openipc_nor_fit=1; drop those, in RAM only, so the command line says only + * what is true. + */ +static void nor_split_bootargs(void) +{ + static const char * const drop[] = { "mtdparts=", "root=", "openipc_nor_fit=" }; + const char *cur = env_get("bootargs"); + char buf[1024], *out = buf; + const char *p; + + if (!cur || strlen(cur) >= sizeof(buf)) + return; + for (p = cur; *p;) { + const char *end = strchr(p, ' '); + size_t len = end ? (size_t)(end - p) : strlen(p); + int keep = len > 0; + int i; + + for (i = 0; keep && i < ARRAY_SIZE(drop); i++) + if (!strncmp(p, drop[i], strlen(drop[i]))) + keep = 0; + if (keep) { + if (out != buf) + *out++ = ' '; + memcpy(out, p, len); + out += len; + } + p += len; + while (*p == ' ') + p++; + } + *out = '\0'; + if (strcmp(buf, cur)) + env_set("bootargs", buf); +} + +/* + * The split layout: a uImage at the kernel offset and the root squashfs at the + * next 64 KiB block after it, in one "firmware" partition the kernel divides + * at boot. The fixed-slot layout also starts with a uImage, but its squashfs + * sits at the slot boundary. + */ +static int nor_split_layout(struct spi_flash *flash, ulong kernel_off, ulong rootfs_off) +{ + image_header_t hdr; + + /* + * A squashfs at a fixed rootfs slot is fixed slots, even when the uImage + * before it ends in the kernel slot's last block and so looks split: a + * kernel from before the split layout needs its mtdparts and root=. A + * split image of that size loses nothing, as the kernel finds the split + * itself and ignores both. + */ + if (rootfs_off - kernel_off == 0x200000 || rootfs_off - kernel_off == 0x300000) + return 0; + if (!kernel_off || !rootfs_off || + spi_flash_read(flash, kernel_off, sizeof(hdr), &hdr) || + !image_check_magic(&hdr)) + return 0; + return rootfs_off == kernel_off + ALIGN(image_get_image_size(&hdr), 0x10000); +} +#endif + int firmware_scan(void) { -#ifdef CONFIG_ENV_IS_IN_NAND +#ifdef CONFIG_FMC_SPI_NAND u8 buf[64]; int ret; @@ -559,13 +763,101 @@ int firmware_scan(void) { scan_spi_device(flash, &kernel_off, &rootfs_off, &kernel_size, &rootfs_size); -#ifndef CONFIG_TARGET_HI3516CV610_FAMILY - if (kernel_size <= 0x200000) - kernel_size = 0x200000; - if (rootfs_size < 0x500000) - rootfs_size = 0x500000; - else if (rootfs_size > 0x500000 && flash->size > 0x800000) - rootfs_size = 0xa00000; +#ifdef CONFIG_OPENIPC_NOR_SPLIT + { + /* fwwrite erases from the firmware start to the end of the chip, as + * recovery does, so no old overlay is left behind a shorter image */ + char str[32]; + const char *ka = env_get("kernaddr"); + ulong start = kernel_off ? kernel_off : + (ka ? hextoul(ka, NULL) : CONFIG_ENV_KERNADDR); + + if (start < flash->size) { + sprintf(str, "0x%lx", (ulong)(flash->size - start)); + env_set("fwsize", str); + /* RAM only, like the rest of the scan: a saved environment keeps an + * older fwwrite that erased only the file's length */ + env_set("fwwrite", "sf probe 0; sf erase ${kernaddr} ${fwsize}; " + "sf write ${baseaddr} ${kernaddr} ${filesize}"); + } + } +#endif + + /* + * Fixed slots -- the rootfs at the 2 or 3 MiB slot boundary after the + * kernel, or nothing found to say otherwise -- get the slots' sizes, so the + * rootfs partition is its whole slot and rootfs_data starts after it. A + * rootfs packed right after the kernel (a FIT and its squashfs written as + * one image) keeps the sizes found: a slot there would put the rootfs + * partition past the squashfs. + * + * The 10 MiB rootfs slot is the 3 MiB kernel slot's (the ultimate pair, + * rootfs at 0x350000; ur moves a large rootfs there before it writes). A + * rootfs over 5 MiB behind the 2 MiB kernel slot is a packed image that + * outgrew the lite slot -- hi3516cv6xx ultimate on 16 MiB, 7104k at + * 0x250000 -- and keeps the size found: rounding it up to 10 MiB moved + * rootfs_data from 0x940000 to 0xc50000, away from the settings there. + */ + if (!kernel_size || kernel_size == 0x200000 || kernel_size == 0x300000) { + if (kernel_size <= 0x200000) + kernel_size = 0x200000; + if (rootfs_size < 0x500000) + rootfs_size = 0x500000; + else if (kernel_size == 0x300000 && rootfs_size < 0xa00000 && flash->size > 0x800000) + rootfs_size = 0xa00000; + } + +#ifdef CONFIG_OPENIPC_NOR_SPLIT + if (nor_split_layout(flash, kernel_off, rootfs_off)) { + char str[32], mtds[128], mtdparts[160]; + const char *mtdids = env_get("mtdids"); + ulong kern_slot, root_slot; + + /* + * kernsize is what loadkernel reads, and also what uk erases; rootaddr + * and rootsize are where ur writes. Those commands install a uImage and a + * squashfs, i.e. fixed slots, so they get the fixed slots: the kernel + * slot whole (the split squashfs starts inside it, and a head left there + * would be found by the next scan before the new rootfs), the rootfs slot + * after it. The kernel slot holds the whole uImage either way. + */ + if (rootfs_off - kernel_off <= 0x200000) { + kern_slot = 0x200000; + root_slot = 0x500000; + } else if (rootfs_off - kernel_off <= 0x300000 && flash->size > 0x800000) { + kern_slot = 0x300000; + root_slot = 0xa00000; + } else { + /* No fixed-slot layout holds this kernel. */ + kern_slot = rootfs_off - kernel_off; + root_slot = flash->size - rootfs_off; + } + spi_flash_free(flash); + sprintf(str, "0x%08lx", kernel_off); + env_set("kernaddr", str); + sprintf(str, "0x%lx", kern_slot); + env_set("kernsize", str); + sprintf(str, "0x%08lx", kernel_off + kern_slot); + env_set("rootaddr", str); + sprintf(str, "0x%lx", root_slot); + env_set("rootsize", str); +#ifdef SFC + if (!mtdids || !*mtdids) + mtdids = SFC; +#endif + /* U-Boot's own idea of the flash; the kernel is not told. */ + sprintf(mtds, "%dk(u-boot),%dk(env),-(firmware)", + CONFIG_ENV_OFFSET / 1024, CONFIG_ENV_SIZE / 1024); + env_set("mtds", mtds); + sprintf(mtdparts, "%s:%s", mtdids ? mtdids : "", mtds); + env_set("mtdparts", mtdparts); + /* bootm checks the uImage's data CRC; a verify=n saved by an older + * bootloader must not skip it. RAM only, like the rest of the scan. */ + env_set("verify", NULL); + nor_split_bootargs(); + env_set("bootfail", NULL); + return 0; + } #endif spi_flash_free(flash); diff --git a/drivers/mtd/spi/gk_sflash.c b/drivers/mtd/spi/gk_sflash.c index bf2f056b4052..6a3b36839ba7 100644 --- a/drivers/mtd/spi/gk_sflash.c +++ b/drivers/mtd/spi/gk_sflash.c @@ -156,6 +156,70 @@ static int sf_program(u32 addr, const u8 *buf, unsigned int words) return sf_wait_ready(); } +/* The boot ROM header's first word, "GK71" */ +#define GK_BOOT_MAGIC 0x474b3731 + +/* + * The boot ROM reads the boot image as raw chip bytes, while everything + * read or written through the controller is packed MSB first: a 32-bit + * word goes to the chip byte-swapped and comes back swapped again. For the + * kernel, the rootfs and the environment that round trip is invisible. + * For the boot image it is not: a programmer writes it in the chip's + * order, and the vendor U-Boot's flash write recognises such an image by + * its byte-swapped magic and swaps it back (check_magic_number() in its + * gk7101_sflash.c). + * + * This driver does the same, both ways, for every caller: from offset 0 + * to sf_swap_end -- header table plus the U-Boot length the header gives + * -- data is swapped on write and on read, so the boot image reads back + * as the file a programmer writes, byte for byte. The window is the image + * on the flash at probe, and a write at 0 moves it: one that starts with + * the swapped magic opens it as long as that image, however the caller + * splits the rest; any other write at 0 closes it. + * + * A write at 0 that starts with the swapped magic and runs past the boot + * image -- a programmer's whole-chip dump in one sf write, or recovery + * image -- is chip order throughout, and is swapped over its whole + * length. Split into sectors (sf update), only its first sector is seen + * at 0, so only the boot image is swapped back. + */ +static u32 sf_swap_end; + +/* The length of a chip-order boot image starting at buf, or 0 */ +static u32 sf_boot_len(const u_char *buf, size_t len) +{ + u32 nwords, i; + + if (len < 32 || get_unaligned_le32(buf) != swab32(GK_BOOT_MAGIC)) + return 0; + /* + * Header: 7 words, then the table -- writes, + * 0xFFFFFFFE polls, 0xFFFFFFFF delays -- + * ended by a 0 word; then nwords of U-Boot. A value can be 0, so the + * table is walked entry by entry. + */ + nwords = swab32(get_unaligned_le32(buf + 8)); + for (i = 7 * 4; i + 4 <= len; ) { + u32 w = swab32(get_unaligned_le32(buf + i)); + + if (!w) + break; + i += 4 * (w == 0xfffffffe ? 3 : 2); + } + if (i + 4 > len) + return 0; + return i + 4 + 4 * nwords; +} + +static void sf_swap_detect(loff_t to, size_t len, const u_char *buf) +{ + if (to) + return; + sf_swap_end = sf_boot_len(buf, len); + if (sf_swap_end) + printf("SF: boot image in the chip's byte order, swapping it back\n"); +} + static int gk_sf_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -175,6 +239,10 @@ static int gk_sf_read(struct mtd_info *mtd, loff_t from, size_t len, u32 w = readl(SF_DATA); unsigned int n = min_t(size_t, len, 4 - skip); + if (addr < sf_swap_end) + w = swab32(w); + addr += 4; + if (!skip && n == 4) { put_unaligned_le32(w, buf); } else { @@ -190,30 +258,6 @@ static int gk_sf_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } -/* The boot ROM header's first word, "GK71" */ -#define GK_BOOT_MAGIC 0x474b3731 - -/* - * An image as a programmer writes it, every word as the chip holds it, - * starts with the boot header magic byte-swapped. Written at 0 as it is, it - * would land swapped twice. The vendor U-Boot's sf write swaps such an - * image back in RAM first, so one boot image serves a programmer and sf - * write alike; the sf command does the same through this, for the whole - * buffer at once -- sf update writes it one sector at a time. - */ -void gk_sflash_boot_order(unsigned int offset, unsigned long len, void *buf) -{ - u8 *p = buf; - size_t i; - - if (offset || len < 4 || len % 4 || - get_unaligned_le32(p) != swab32(GK_BOOT_MAGIC)) - return; - printf("SF: boot image in flash byte order, swapping it back\n"); - for (i = 0; i < len; i += 4) - put_unaligned_le32(swab32(get_unaligned_le32(p + i)), p + i); -} - /* * The controller programs whole words, so a write that starts or ends * inside one pads it with 0xff, which leaves those flash bytes as they are. @@ -224,10 +268,14 @@ static int gk_sf_write(struct mtd_info *mtd, loff_t to, size_t len, u8 page[SZ_PAGE]; u32 addr = to & ~3; unsigned int head = to & 3; + unsigned int i; + u32 end; int ret; if (to + len > mtd->size) return -EINVAL; + sf_swap_detect(to, len, buf); + end = !to && sf_swap_end ? max_t(u32, sf_swap_end, len) : sf_swap_end; *retlen = 0; while (len) { unsigned int room = SZ_PAGE - (addr & (SZ_PAGE - 1)); @@ -236,6 +284,9 @@ static int gk_sf_write(struct mtd_info *mtd, loff_t to, size_t len, memset(page, 0xff, span); memcpy(page + head, buf, n); + for (i = 0; i < span && addr + i < end; i += 4) + put_unaligned_le32(swab32(get_unaligned_le32(page + i)), + page + i); ret = sf_program(addr, page, span / 4); if (ret) return ret; @@ -269,6 +320,21 @@ static int gk_sf_erase(struct mtd_info *mtd, struct erase_info *instr) static struct spi_flash gk_flash; static int gk_announced; +/* The swap window over the boot image already on the flash */ +static void sf_swap_probe(u32 size) +{ + u8 hdr[SZ_2K]; + size_t n; + unsigned int i; + + sf_swap_end = 0; + gk_sf_read(&gk_flash.mtd, 0, sizeof(hdr), &n, hdr); + for (i = 0; i < sizeof(hdr); i += 4) + put_unaligned_le32(swab32(get_unaligned_le32(hdr + i)), + hdr + i); + sf_swap_end = min(sf_boot_len(hdr, sizeof(hdr)), size); +} + struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode) { @@ -310,6 +376,9 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, mtd->_write = gk_sf_write; mtd->_erase = gk_sf_erase; mtd->priv = flash; + /* Once: later writes at 0 keep the window up to date */ + if (!gk_announced) + sf_swap_probe(size); /* Once: the environment and common/openipc.c probe again and again */ if (!gk_announced++) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index ee23839033ab..23f030b47cc5 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -362,7 +362,8 @@ static void _dw_eth_halt(struct dw_eth_dev *priv) writel(readl(&mac_p->conf) & ~(RXENABLE | TXENABLE), &mac_p->conf); writel(readl(&dma_p->opmode) & ~(RXSTART | TXSTART), &dma_p->opmode); - phy_shutdown(priv->phydev); + if (priv->phydev) + phy_shutdown(priv->phydev); } int designware_eth_init(struct dw_eth_dev *priv, u8 *enetaddr) @@ -372,6 +373,10 @@ int designware_eth_init(struct dw_eth_dev *priv, u8 *enetaddr) unsigned int start; int ret; + /* No PHY was found at initialisation: no network, rather than a crash */ + if (!priv->phydev) + return -ENODEV; + writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode); /* diff --git a/openipc/gk7101.rvs b/openipc/gk7101.rvs index 0a931078c8b4..1f608bd87590 100644 --- a/openipc/gk7101.rvs +++ b/openipc/gk7101.rvs @@ -1,8 +1,12 @@ # GK7101 boot ROM set-up: the register writes the mask ROM makes before it # copies U-Boot into DDR -- eFuse, DDR PHY, the RCT and the DDR controller. # Taken from the header of the SDK's prebuilt uboot-gk7101-rb-ov2710_v2.2.bin -# (GK710X_LinuxSDK_v2.0.0 boot/prebuild), the table on GK710x cameras in the -# field. build.sh writes it into the boot header. +# (GK710X_LinuxSDK_v2.0.0 boot/prebuild), with the eFuse lines as GK7102 +# cameras in the field have them: the SDK images also write 0xFF39C119 to +# 0x6000B100 first and start the eFuse load at the end without waiting for +# it; the vendor U-Boots of 2016-2017 found on cameras do neither, and a +# GK7102 with the SDK's table did not start. build.sh writes it into the +# boot header. # # version: header word 1 # ddrtest:
= header words 5 and 6 @@ -13,7 +17,6 @@ version: 0x30310801 ddrtest: 0xC4000000 = 0x5A5A5A5A # -setup: 0x6000B100 = 0xFF39C119 setup: 0x60021A68 = 0x00000300 setup: 0x60021A60 = 0x00002B52 setup: 0x60021A64 = 0x00000035 @@ -75,3 +78,4 @@ rdcom: 0x60004018 = 0x01010000 setup: 0x6000401C = 0x00000210 setup: 0x60004000 = 0x00000007 setup: 0x6000B104 = 0x00000001 +rdcom: 0x6000B104 = 0x00000003 diff --git a/openipc/gk7102.rvs b/openipc/gk7102.rvs index f66dd0133b7d..7d8c696c8e97 100644 --- a/openipc/gk7102.rvs +++ b/openipc/gk7102.rvs @@ -1,8 +1,12 @@ # GK7102 boot ROM set-up: the register writes the mask ROM makes before it # copies U-Boot into DDR -- eFuse, DDR PHY, the RCT and the DDR controller. # Taken from the header of the SDK's prebuilt uboot-gk7102-rb-jxh42_v2.0.bin -# (GK710X_LinuxSDK_v2.0.0 boot/prebuild), the table on GK710x cameras in the -# field. build.sh writes it into the boot header. +# (GK710X_LinuxSDK_v2.0.0 boot/prebuild), with the eFuse lines as GK7102 +# cameras in the field have them: the SDK images also write 0xFF39C119 to +# 0x6000B100 first and start the eFuse load at the end without waiting for +# it; the vendor U-Boots of 2016-2017 found on cameras do neither, and a +# GK7102 with the SDK's table did not start. build.sh writes it into the +# boot header. # # version: header word 1 # ddrtest:
= header words 5 and 6 @@ -13,7 +17,6 @@ version: 0x30310801 ddrtest: 0xC4000000 = 0x5A5A5A5A # -setup: 0x6000B100 = 0xFF39C119 setup: 0x60021A68 = 0x00000300 setup: 0x60021A60 = 0x00002B52 setup: 0x60021A64 = 0x00000035 @@ -75,3 +78,4 @@ rdcom: 0x60004018 = 0x01010000 setup: 0x6000401C = 0x00000210 setup: 0x60004000 = 0x00000007 setup: 0x6000B104 = 0x00000001 +rdcom: 0x6000B104 = 0x00000003 diff --git a/openipc/openipc_config b/openipc/openipc_config index 3ddf3089b22a..bfcaea3e3297 100644 --- a/openipc/openipc_config +++ b/openipc/openipc_config @@ -78,6 +78,28 @@ CONFIG_CMD_MEMORY=y CONFIG_CMD_CRC32=y CONFIG_CMD_SLEEP=y +# SD card: recovery images and scripts (common/openipc.c); the only way +# back for a camera without Ethernet +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +# CONFIG_MMC_HW_PARTITIONING is not set +CONFIG_CMD_MMC=y +CONFIG_DOS_PARTITION=y +CONFIG_FS_FAT=y +CONFIG_CMD_FAT=y + +# Room for it in the 256 KiB boot partition, with no Thumb-2 on ARM1176: +# link-time optimisation, and no commands the OpenIPC flows have no use for +# here (these kernels take no device tree) +CONFIG_LTO=y +# CONFIG_CMD_FDT is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_NFS is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_CONSOLE is not set + # Network: the GMAC is a DesignWare MAC, the PHY the SoC's own CONFIG_NET=y CONFIG_NETDEVICES=y @@ -87,6 +109,5 @@ CONFIG_CMD_NET=y CONFIG_CMD_TFTPBOOT=y CONFIG_CMD_DHCP=y CONFIG_CMD_PING=y -CONFIG_CMD_MII=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_LIB_RAND=y