From 2999b9177fe8977d85c3d8cff7ac6a6d4a63c6e1 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sat, 19 Feb 2022 15:32:17 +0000 Subject: [PATCH] put together coldboot startup firmware --- coldboot/Makefile | 56 ++++++++++++++ coldboot/coldboot.c | 157 ++++++++++++++++++++++++++++++++++++++ coldboot/head.S | 107 ++++++++++++++++++++++++++ coldboot/powerpc.lds | 13 ++++ libgram/include/gram.h | 41 ++++++++++ libgram/src/calibration.c | 137 +++++++++++++++++++++++++++++++++ libgram/src/dfii.c | 93 ++++++++++++++++++++++ libgram/src/dfii.h | 23 ++++++ libgram/src/helpers.h | 11 +++ libgram/src/hw_regs.h | 24 ++++++ libgram/src/init.c | 15 ++++ 11 files changed, 677 insertions(+) create mode 100644 coldboot/Makefile create mode 100644 coldboot/coldboot.c create mode 100644 coldboot/head.S create mode 100644 coldboot/powerpc.lds create mode 100644 libgram/include/gram.h create mode 100644 libgram/src/calibration.c create mode 100644 libgram/src/dfii.c create mode 100644 libgram/src/dfii.h create mode 100644 libgram/src/helpers.h create mode 100644 libgram/src/hw_regs.h create mode 100644 libgram/src/init.c diff --git a/coldboot/Makefile b/coldboot/Makefile new file mode 100644 index 0000000..ef71b65 --- /dev/null +++ b/coldboot/Makefile @@ -0,0 +1,56 @@ +ARCH = $(shell uname -m) +ifneq ("$(ARCH)", "ppc64") +ifneq ("$(ARCH)", "ppc64le") + CROSS_COMPILE ?= powerpc64le-linux-gnu- +endif +endif + +LIBGRAMDIR = ../libgram +LIBGRAMINC = ../libgram/include + +GRAMOBJS := $(LIBGRAMDIR)/src/init.o \ + $(LIBGRAMDIR)/src/dfii.o \ + $(LIBGRAMDIR)/src/calibration.o + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS = -Os -g -Wall -std=c99 -msoft-float -mno-string \ + -mno-multiple -mno-vsx -mno-altivec -mlittle-endian \ + -fno-stack-protector -mstrict-align -ffreestanding \ + -fdata-sections -ffunction-sections -I../include \ + -I $(LIBGRAMINC) +ASFLAGS = $(CFLAGS) +LDFLAGS = -T powerpc.lds + +all: coldboot.hex + +console.o: ../lib/console.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +init.o: ../libgram/src/init.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +dfii.o: ../libgram/src/dfii.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +calibration.o: ../libgram/src/calibration.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +coldboot.elf: coldboot.o head.o ../lib/console.o $(GRAMOBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +coldboot.bin: coldboot.elf + $(OBJCOPY) -O binary $^ $@ + +coldboot.hex: coldboot.bin + ../scripts/bin2hex.py $^ > $@ + powerpc64le-linux-gnu-objdump -D coldboot.elf > coldboot.as + + +clean: + @rm -f *.o coldboot.elf coldboot.bin coldboot.hex coldboot.as +distclean: clean + rm -f *~ + diff --git a/coldboot/coldboot.c b/coldboot/coldboot.c new file mode 100644 index 0000000..3026442 --- /dev/null +++ b/coldboot/coldboot.c @@ -0,0 +1,157 @@ +#include +#include + +#include "console.h" + +#include +#include +#include + +static inline uint32_t read32(const void *addr) +{ + return *(volatile uint32_t *)addr; +} + +static inline void write32(void *addr, uint32_t value) +{ + *(volatile uint32_t *)addr = value; +} + +struct uart_regs { + uint32_t divisor; + uint32_t rx_data; + uint32_t rx_rdy; + uint32_t rx_err; + uint32_t tx_data; + uint32_t tx_rdy; + uint32_t zero0; // reserved + uint32_t zero1; // reserved + uint32_t ev_status; + uint32_t ev_pending; + uint32_t ev_enable; +}; + +void memcpy(void *dest, void *src, size_t n) { + int i; + //cast src and dest to char* + char *src_char = (char *)src; + char *dest_char = (char *)dest; + for (i=0; i> 4) & 0xF]); + putchar(lut[val_arr[3-i] & 0xF]); + } +} + +void isr(void) { + +} + +int main(void) { + const int kNumIterations = 65536; + int res, failcnt = 0; + uint32_t tmp; + volatile uint32_t *ram = 0x10000000; + console_init(); + puts("Firmware launched...\n"); + + puts("DRAM init... "); + struct gramCtx ctx; + struct gramProfile profile = { + .mode_registers = { + 0x320, 0x6, 0x200, 0x0 + }, + .rdly_p0 = 2, + .rdly_p1 = 2, + }; + struct gramProfile profile2; + gram_init(&ctx, &profile, (void*)0x10000000, + (void*)0x00009000, + (void*)0x00008000); + puts("done\n"); + + puts("Rdly\np0: "); + for (size_t i = 0; i < 8; i++) { + profile2.rdly_p0 = i; + gram_load_calibration(&ctx, &profile2); + gram_reset_burstdet(&ctx); + for (size_t j = 0; j < 128; j++) { + tmp = ram[j]; + } + if (gram_read_burstdet(&ctx, 0)) { + puts("1"); + } else { + puts("0"); + } + } + puts("\n"); + + puts("Rdly\np1: "); + for (size_t i = 0; i < 8; i++) { + profile2.rdly_p1 = i; + gram_load_calibration(&ctx, &profile2); + gram_reset_burstdet(&ctx); + for (size_t j = 0; j < 128; j++) { + tmp = ram[j]; + } + if (gram_read_burstdet(&ctx, 1)) { + puts("1"); + } else { + puts("0"); + } + } + puts("\n"); + + puts("Auto calibrating... "); + res = gram_generate_calibration(&ctx, &profile2); + if (res != GRAM_ERR_NONE) { + puts("failed\n"); + gram_load_calibration(&ctx, &profile); + } else { + gram_load_calibration(&ctx, &profile2); + } + puts("done\n"); + + puts("Auto calibration profile:"); + puts("p0 rdly:"); + uart_writeuint32(profile2.rdly_p0); + puts(" p1 rdly:"); + uart_writeuint32(profile2.rdly_p1); + puts("\n"); + + puts("DRAM test... \n"); + for (size_t i = 0; i < kNumIterations; i++) { + ram[i] = 0xDEAF0000 | i*4; + } + + for (size_t i = 0; i < kNumIterations; i++) { + if (ram[i] != (0xDEAF0000 | i*4)) { + puts("fail : *(0x"); + uart_writeuint32(&ram[i]); + puts(") = "); + uart_writeuint32(ram[i]); + putchar('\n'); + failcnt++; + + if (failcnt > 10) { + puts("Test canceled (more than 10 errors)\n"); + break; + } + } + } + puts("done\n"); + + while (1); + + return 0; +} + diff --git a/coldboot/head.S b/coldboot/head.S new file mode 100644 index 0000000..104cb7e --- /dev/null +++ b/coldboot/head.S @@ -0,0 +1,107 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x2900 + +#define FIXUP_ENDIAN \ + tdi 0,0,0x48; /* Reverse endian of b . + 8 */ \ + b 191f; /* Skip trampoline if endian is good */ \ + .long 0xa600607d; /* mfmsr r11 */ \ + .long 0x01006b69; /* xori r11,r11,1 */ \ + .long 0x05009f42; /* bcl 20,31,$+4 */ \ + .long 0xa602487d; /* mflr r10 */ \ + .long 0x14004a39; /* addi r10,r10,20 */ \ + .long 0xa64b5a7d; /* mthsrr0 r10 */ \ + .long 0xa64b7b7d; /* mthsrr1 r11 */ \ + .long 0x2402004c; /* hrfid */ \ +191: + + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* + * Microwatt currently enters in LE mode at 0x0, so we don't need to + * do any endian fix ups> + */ + . = 0 +.global _start +_start: + b boot_entry + + /* QEMU enters at 0x10 */ + . = 0x10 + FIXUP_ENDIAN + b boot_entry + + . = 0x100 + FIXUP_ENDIAN + b boot_entry + +.global boot_entry +boot_entry: + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12, + bctrl + b . + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + EXCEPTION(0x500) + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + EXCEPTION(0xe00) + EXCEPTION(0xe20) + EXCEPTION(0xe40) + EXCEPTION(0xe60) + EXCEPTION(0xe80) + EXCEPTION(0xf00) + EXCEPTION(0xf20) + EXCEPTION(0xf40) + EXCEPTION(0xf60) + EXCEPTION(0xf80) +#if 0 + EXCEPTION(0x1000) + EXCEPTION(0x1100) + EXCEPTION(0x1200) + EXCEPTION(0x1300) + EXCEPTION(0x1400) + EXCEPTION(0x1500) + EXCEPTION(0x1600) +#endif diff --git a/coldboot/powerpc.lds b/coldboot/powerpc.lds new file mode 100644 index 0000000..5e2f317 --- /dev/null +++ b/coldboot/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x2c00; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/libgram/include/gram.h b/libgram/include/gram.h new file mode 100644 index 0000000..401713e --- /dev/null +++ b/libgram/include/gram.h @@ -0,0 +1,41 @@ +#ifndef GRAM_H +#define GRAM_H + +#include +#include +#include + +enum GramError { + GRAM_ERR_NONE = 0, + GRAM_ERR_UNDOCUMENTED, + GRAM_ERR_RDLY_MAX, +}; + +struct gramCoreRegs; +struct gramPHYRegs; +struct gramCtx { + volatile void *ddr_base; + volatile struct gramCoreRegs *core; + volatile struct gramPHYRegs *phy; + void *user_data; +}; + +struct gramProfile { + uint8_t rdly_p0; + uint8_t rdly_p1; + uint32_t mode_registers[4]; +}; + +extern __attribute__((visibility ("default"))) int gram_init(struct gramCtx *ctx, const struct gramProfile *profile, void *ddr_base, void *core_base, void *phy_base); +extern __attribute__((visibility ("default"))) int gram_generate_calibration(const struct gramCtx *ctx, struct gramProfile *profile); +extern __attribute__((visibility ("default"))) void gram_load_calibration(const struct gramCtx *ctx, const struct gramProfile *profile); + +extern __attribute__((visibility ("default"))) void gram_reset_burstdet(const struct gramCtx *ctx); +extern __attribute__((visibility ("default"))) bool gram_read_burstdet(const struct gramCtx *ctx, int phase); + +#ifdef GRAM_RW_FUNC +extern uint32_t gram_read(const struct gramCtx *ctx, void *addr); +extern int gram_write(const struct gramCtx *ctx, void *addr, uint32_t value); +#endif /* GRAM_RW_FUNC */ + +#endif /* GRAM_H */ diff --git a/libgram/src/calibration.c b/libgram/src/calibration.c new file mode 100644 index 0000000..a77c44a --- /dev/null +++ b/libgram/src/calibration.c @@ -0,0 +1,137 @@ +#include +#include + +#include "hw_regs.h" +#include +#include "dfii.h" +#include "helpers.h" + +static void set_rdly(const struct gramCtx *ctx, unsigned int phase, unsigned int rdly) { +#ifdef GRAM_RW_FUNC + if (phase == 0) { + gram_write(ctx, (void*)&(ctx->phy->rdly_p0), rdly); + } else if (phase == 1) { + gram_write(ctx, (void*)&(ctx->phy->rdly_p1), rdly); + } +#else + if (phase == 0) { + ctx->phy->rdly_p0 = rdly; + } else if (phase == 1) { + ctx->phy->rdly_p1 = rdly; + } +#endif +} + +void gram_reset_burstdet(const struct gramCtx *ctx) { +#ifdef GRAM_RW_FUNC + gram_write(ctx, (void*)&(ctx->phy->burstdet), 0); +#else + ctx->phy->burstdet = 0; +#endif +} + +bool gram_read_burstdet(const struct gramCtx *ctx, int phase) { +#ifdef GRAM_RW_FUNC + return !!(gram_read(ctx, (void*)&(ctx->phy->burstdet)) & (1 << phase)); +#else + return !!(ctx->phy->burstdet & (1 << phase)); +#endif +} + +int gram_generate_calibration(const struct gramCtx *ctx, struct gramProfile *profile) { + unsigned char rdly; + unsigned char min_rdly_p0, min_rdly_p1; + unsigned char max_rdly_p0 = 7, max_rdly_p1 = 7; + uint32_t tmp; + volatile uint32_t *ram = ctx->ddr_base; + size_t i; + + dfii_setsw(ctx, true); + + (void)tmp; + + // Find minimal rdly + for (rdly = 0; rdly < 8; rdly++) { + profile->rdly_p0 = rdly; + gram_load_calibration(ctx, profile); + gram_reset_burstdet(ctx); + + for (i = 0; i < 128; i++) { + tmp = ram[i]; + } + + if (gram_read_burstdet(ctx, 0)) { + min_rdly_p0 = rdly; + break; + } else if (rdly == 7) { + return GRAM_ERR_RDLY_MAX; + } + } + + for (rdly = 0; rdly < 8; rdly++) { + profile->rdly_p1 = rdly; + gram_load_calibration(ctx, profile); + gram_reset_burstdet(ctx); + + for (i = 0; i < 128; i++) { + tmp = ram[i]; + } + + if (gram_read_burstdet(ctx, 1)) { + min_rdly_p1 = rdly; + break; + } else if (rdly == 7) { + return GRAM_ERR_RDLY_MAX; + } + } + + // Find maximal rdly + for (rdly = min_rdly_p0+1; rdly < 8; rdly++) { + profile->rdly_p0 = rdly; + gram_load_calibration(ctx, profile); + gram_reset_burstdet(ctx); + + for (i = 0; i < 128; i++) { + tmp = ram[i]; + } + + if (!gram_read_burstdet(ctx, 0)) { + max_rdly_p0 = rdly - 1; + break; + } else if (rdly == 7) { + return GRAM_ERR_RDLY_MAX; + } + } + + for (rdly = min_rdly_p1+1; rdly < 8; rdly++) { + profile->rdly_p1 = rdly; + gram_load_calibration(ctx, profile); + gram_reset_burstdet(ctx); + + for (i = 0; i < 128; i++) { + tmp = ram[i]; + } + + if (!gram_read_burstdet(ctx, 1)) { + max_rdly_p1 = rdly - 1; + break; + } else if (rdly == 7) { + return GRAM_ERR_RDLY_MAX; + } + } + + dfii_setsw(ctx, false); + + // Store average rdly value + profile->rdly_p0 = (min_rdly_p0+max_rdly_p0)/2; + profile->rdly_p1 = (min_rdly_p1+max_rdly_p1)/2; + + return GRAM_ERR_NONE; +} + +void gram_load_calibration(const struct gramCtx *ctx, const struct gramProfile *profile) { + dfii_setsw(ctx, true); + set_rdly(ctx, 0, profile->rdly_p0); + set_rdly(ctx, 1, profile->rdly_p1); + dfii_setsw(ctx, false); +} diff --git a/libgram/src/dfii.c b/libgram/src/dfii.c new file mode 100644 index 0000000..58519bd --- /dev/null +++ b/libgram/src/dfii.c @@ -0,0 +1,93 @@ +#include + +#include "hw_regs.h" +#include +#include "dfii.h" +#include "helpers.h" + +static void dfii_setcontrol(const struct gramCtx *ctx, uint8_t val) { +#ifdef GRAM_RW_FUNC + gram_write(ctx, (void*)&(ctx->core->control), val); +#else + ctx->core->control = val; +#endif +} + +void dfii_setsw(const struct gramCtx *ctx, bool software_control) { + if (software_control) { + dfii_setcontrol(ctx, DFII_CONTROL_CKE|DFII_CONTROL_ODT); + } else { + dfii_setcontrol(ctx, DFII_CONTROL_SEL|DFII_CONTROL_RESET); + } +} + +void dfii_set_p0_address(const struct gramCtx *ctx, uint32_t val) { +#ifdef GRAM_RW_FUNC + gram_write(ctx, (void*)&(ctx->core->phases[0].address), val); +#else + ctx->core->phases[0].address = val; +#endif +} + +void dfii_set_p0_baddress(const struct gramCtx *ctx, uint32_t val) { +#ifdef GRAM_RW_FUNC + gram_write(ctx, (void*)&(ctx->core->phases[0].baddress), val); +#else + ctx->core->phases[0].baddress = val; +#endif +} + +void dfii_p0_command(const struct gramCtx *ctx, uint32_t cmd) { +#ifdef GRAM_RW_FUNC + gram_write(ctx, (void*)&(ctx->core->phases[0].command), cmd); + gram_write(ctx, (void*)&(ctx->core->phases[0].command_issue), 1); +#else + ctx->core->phases[0].command = cmd; + ctx->core->phases[0].command_issue = 1; +#endif +} + +/* Set MRx register */ +static void dfii_set_mr(const struct gramCtx *ctx, uint8_t mr, uint16_t val) { + dfii_set_p0_address(ctx, val); + dfii_set_p0_baddress(ctx, mr); + dfii_p0_command(ctx, DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); +} + +#define MR0_DLL_RESET (1 << 8) +void dfii_initseq(const struct gramCtx *ctx, const struct gramProfile *profile) { + /* Release reset */ + dfii_set_p0_address(ctx, 0x0); + dfii_set_p0_baddress(ctx, 0); + dfii_setcontrol(ctx, DFII_CONTROL_ODT); + cdelay(50000); + + /* Bring CKE high */ + dfii_set_p0_address(ctx, 0x0); + dfii_set_p0_baddress(ctx, 0); + dfii_setcontrol(ctx, DFII_CONTROL_CKE|DFII_CONTROL_ODT); + cdelay(10000); + + /* Load Mode Register 2, CWL=5 */ + dfii_set_mr(ctx, 2, profile->mode_registers[2]); + + /* Load Mode Register 3 */ + dfii_set_mr(ctx, 3, profile->mode_registers[3]); + + /* Load Mode Register 1 */ + dfii_set_mr(ctx, 1, profile->mode_registers[1]); + + /* Load Mode Register 0, CL=6, BL=8 */ + dfii_set_mr(ctx, 0, profile->mode_registers[0]); + if (profile->mode_registers[0] & MR0_DLL_RESET) { + cdelay(100); + dfii_set_mr(ctx, 0, profile->mode_registers[0] & ~MR0_DLL_RESET); + } + cdelay(600); + + /* ZQ Calibration */ + dfii_set_p0_address(ctx, 0x400); + dfii_set_p0_baddress(ctx, 0); + dfii_p0_command(ctx, DFII_COMMAND_WE|DFII_COMMAND_CS); + cdelay(600); +} diff --git a/libgram/src/dfii.h b/libgram/src/dfii.h new file mode 100644 index 0000000..3e84f8c --- /dev/null +++ b/libgram/src/dfii.h @@ -0,0 +1,23 @@ +#ifndef DFII_H +#define DFII_H + +#include + +#define DFII_CONTROL_SEL (1 << 0) +#define DFII_CONTROL_CKE (1 << 1) +#define DFII_CONTROL_ODT (1 << 2) +#define DFII_CONTROL_RESET (1 << 3) + +#define DFII_COMMAND_CS (1 << 0) +#define DFII_COMMAND_WE (1 << 1) +#define DFII_COMMAND_CAS (1 << 2) +#define DFII_COMMAND_RAS (1 << 3) +#define DFII_COMMAND_WRDATA (1 << 4) + +void dfii_setsw(const struct gramCtx *ctx, bool software_control); +void dfii_initseq(const struct gramCtx *ctx, const struct gramProfile *profile); +void dfii_set_p0_address(const struct gramCtx *ctx, uint32_t val); +void dfii_set_p0_baddress(const struct gramCtx *ctx, uint32_t val); +void dfii_p0_command(const struct gramCtx *ctx, uint32_t cmd); + +#endif /* DFII_H */ diff --git a/libgram/src/helpers.h b/libgram/src/helpers.h new file mode 100644 index 0000000..e0f6b0f --- /dev/null +++ b/libgram/src/helpers.h @@ -0,0 +1,11 @@ +#ifndef HELPERS_H +#define HELPERS_H + +__attribute__((unused)) static inline void cdelay(int i) { + while(i > 0) { + __asm__ volatile("nop"); + i--; + } +} + +#endif /* HELPERS_H */ diff --git a/libgram/src/hw_regs.h b/libgram/src/hw_regs.h new file mode 100644 index 0000000..ac0457e --- /dev/null +++ b/libgram/src/hw_regs.h @@ -0,0 +1,24 @@ +#ifndef HW_REGS_H +#define HW_REGS_H + +struct gramPHYRegs { + uint32_t burstdet; + uint32_t rdly_p0; + uint32_t rdly_p1; +} __attribute__((packed)); + +struct DFII_Phase { + uint32_t command; + uint32_t command_issue; + uint32_t address; + uint32_t baddress; + uint32_t wrdata; + uint32_t rddata; +} __attribute__((packed)); + +struct gramCoreRegs { + uint32_t control; + struct DFII_Phase phases[4]; +} __attribute__((packed)); + +#endif /* HW_REGS_H */ \ No newline at end of file diff --git a/libgram/src/init.c b/libgram/src/init.c new file mode 100644 index 0000000..d869f0e --- /dev/null +++ b/libgram/src/init.c @@ -0,0 +1,15 @@ +#include +#include "dfii.h" + +int gram_init(struct gramCtx *ctx, const struct gramProfile *profile, void *ddr_base, void *core_base, void *phy_base) { + ctx->ddr_base = ddr_base; + ctx->core = core_base; + ctx->phy = phy_base; + + dfii_setsw(ctx, true); + dfii_initseq(ctx, profile); + gram_load_calibration(ctx, profile); + dfii_setsw(ctx, false); + + return GRAM_ERR_NONE; +} -- 2.30.2