diff options
Diffstat (limited to 'arch/arm/boards/novena')
-rw-r--r-- | arch/arm/boards/novena/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/boards/novena/board.c | 109 | ||||
-rw-r--r-- | arch/arm/boards/novena/ddr_regs.h | 119 | ||||
-rw-r--r-- | arch/arm/boards/novena/flash-header-novena.imxcfg | 6 | ||||
-rw-r--r-- | arch/arm/boards/novena/lowlevel.c | 173 |
5 files changed, 411 insertions, 0 deletions
diff --git a/arch/arm/boards/novena/Makefile b/arch/arm/boards/novena/Makefile new file mode 100644 index 0000000000..3111392bf9 --- /dev/null +++ b/arch/arm/boards/novena/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +obj-y += board.o +lwl-y += lowlevel.o diff --git a/arch/arm/boards/novena/board.c b/arch/arm/boards/novena/board.c new file mode 100644 index 0000000000..b6c59aff44 --- /dev/null +++ b/arch/arm/boards/novena/board.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 John Watts <contact@jookia.org> + +#include <common.h> +#include <deep-probe.h> +#include <fs.h> +#include <libfile.h> +#include <net.h> + +struct novena_eeprom { + uint8_t signature[6]; /* 'Novena' */ + uint8_t version; /* 1 or 2, not checked */ + uint8_t page_size; /* v2 only: EEPROM read/write page */ + uint32_t serial; /* 32-bit serial number */ + uint8_t mac[6]; /* Gigabit MAC address */ + uint16_t features; /* features */ + /* ... extra fields omitted ... */ +} __packed; + +static void power_on_audio_codec(void) +{ + int rc = of_devices_ensure_probed_by_name("regulator-audio-codec"); + + if (rc < 0) + pr_err("Unable to power on audio codec: %s\n", strerror(-rc)); +} + +static struct novena_eeprom *novena_read_eeprom(void) +{ + size_t read; + loff_t max = sizeof(struct novena_eeprom); + void *eeprom; + int rc; + + /* + * When powered off the audio codec pulls down the EEPROM's I2C line. + * Power it on so we can actually read data. + */ + power_on_audio_codec(); + + rc = of_device_ensure_probed_by_alias("eeprom0"); + if (rc < 0) { + pr_err("Unable to probe eeprom0: %s\n", strerror(-rc)); + return NULL; + } + + rc = read_file_2("/dev/eeprom0", &read, &eeprom, max); + + if (rc < 0 && rc != -EFBIG) { + pr_err("Unable to read Novena EEPROM: %s\n", strerror(-rc)); + return NULL; + } else if (read != max) { + pr_err("Short read from Novena EEPROM?\n"); + free(eeprom); + return NULL; + } + + return eeprom; +} + +static bool novena_check_eeprom(struct novena_eeprom *eeprom) +{ + char *sig = eeprom->signature; + size_t size = sizeof(eeprom->signature); + + if (memcmp("Novena", sig, size) != 0) { + pr_err("Unknown Novena EEPROM signature\n"); + return false; + } + + return true; +} + +static void novena_set_mac(struct novena_eeprom *eeprom) +{ + struct device_node *dnode; + + dnode = of_find_node_by_alias(of_get_root_node(), "ethernet0"); + if (dnode) + of_eth_register_ethaddr(dnode, eeprom->mac); + else + pr_err("Unable to find ethernet node\n"); +} + +static int novena_probe(struct device *dev) +{ + struct novena_eeprom *eeprom = novena_read_eeprom(); + + if (eeprom && novena_check_eeprom(eeprom)) + novena_set_mac(eeprom); + + free(eeprom); + + return 0; +} + +static const struct of_device_id novena_of_match[] = { + { .compatible = "kosagi,imx6q-novena", }, + { /* sentinel */ } +}; + +static struct driver novena_board_driver = { + .name = "board-novena", + .probe = novena_probe, + .of_compatible = novena_of_match, +}; +coredevice_platform_driver(novena_board_driver); + +BAREBOX_DEEP_PROBE_ENABLE(novena_of_match); diff --git a/arch/arm/boards/novena/ddr_regs.h b/arch/arm/boards/novena/ddr_regs.h new file mode 100644 index 0000000000..5f18d5e0e4 --- /dev/null +++ b/arch/arm/boards/novena/ddr_regs.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2014 Marek Vasut <marex@denx.de> */ + +#ifndef NOVENA_DDR_REGS_H +#define NOVENA_DDR_REGS_H + +/* MEMORY CONTROLLER CONFIGURATION */ + +static struct mx6dq_iomux_ddr_regs novena_ddr_regs = { + /* SDCLK[0:1], CAS, RAS, Reset: Differential input, 40ohm */ + .dram_sdclk_0 = 0x00020038, + .dram_sdclk_1 = 0x00020038, + .dram_cas = 0x00000038, + .dram_ras = 0x00000038, + .dram_reset = 0x00000038, + /* SDCKE[0:1]: 100k pull-up */ + .dram_sdcke0 = 0x00003000, + .dram_sdcke1 = 0x00003000, + /* SDBA2: pull-up disabled */ + .dram_sdba2 = 0x00000000, + /* SDODT[0:1]: 100k pull-up, 40 ohm */ + .dram_sdodt0 = 0x00000038, + .dram_sdodt1 = 0x00000038, + /* SDQS[0:7]: Differential input, 40 ohm */ + .dram_sdqs0 = 0x00000038, + .dram_sdqs1 = 0x00000038, + .dram_sdqs2 = 0x00000038, + .dram_sdqs3 = 0x00000038, + .dram_sdqs4 = 0x00000038, + .dram_sdqs5 = 0x00000038, + .dram_sdqs6 = 0x00000038, + .dram_sdqs7 = 0x00000038, + + /* DQM[0:7]: Differential input, 40 ohm */ + .dram_dqm0 = 0x00000038, + .dram_dqm1 = 0x00000038, + .dram_dqm2 = 0x00000038, + .dram_dqm3 = 0x00000038, + .dram_dqm4 = 0x00000038, + .dram_dqm5 = 0x00000038, + .dram_dqm6 = 0x00000038, + .dram_dqm7 = 0x00000038, +}; + +static struct mx6dq_iomux_grp_regs novena_grp_regs = { + /* DDR3 */ + .grp_ddr_type = 0x000c0000, + .grp_ddrmode_ctl = 0x00020000, + /* Disable DDR pullups */ + .grp_ddrpke = 0x00000000, + /* ADDR[00:16], SDBA[0:1]: 40 ohm */ + .grp_addds = 0x00000038, + /* CS0/CS1/SDBA2/CKE0/CKE1/SDWE: 40 ohm */ + .grp_ctlds = 0x00000038, + /* DATA[00:63]: Differential input, 40 ohm */ + .grp_ddrmode = 0x00020000, + .grp_b0ds = 0x00000038, + .grp_b1ds = 0x00000038, + .grp_b2ds = 0x00000038, + .grp_b3ds = 0x00000038, + .grp_b4ds = 0x00000038, + .grp_b5ds = 0x00000038, + .grp_b6ds = 0x00000038, + .grp_b7ds = 0x00000038, +}; + +/* MEMORY STICK CONFIGURATION */ + +static struct mx6_mmdc_calibration novena_mmdc_calib = { + /* write leveling calibration determine */ + .p0_mpwldectrl0 = 0x00420048, + .p0_mpwldectrl1 = 0x006f0059, + .p1_mpwldectrl0 = 0x005a0104, + .p1_mpwldectrl1 = 0x01070113, + /* Read DQS Gating calibration */ + .p0_mpdgctrl0 = 0x437c040b, + .p0_mpdgctrl1 = 0x0413040e, + .p1_mpdgctrl0 = 0x444f0446, + .p1_mpdgctrl1 = 0x044d0422, + /* Read Calibration: DQS delay relative to DQ read access */ + .p0_mprddlctl = 0x4c424249, + .p1_mprddlctl = 0x4e48414f, + /* Write Calibration: DQ/DM delay relative to DQS write access */ + .p0_mpwrdlctl = 0x42414641, + .p1_mpwrdlctl = 0x46374b43, +}; + +static struct mx6_ddr_sysinfo novena_ddr_info = { + /* Width of data bus: 0=16, 1=32, 2=64 */ + .dsize = 2, + /* Config for full 4GB range so that get_mem_size() works */ + .cs_density = 32, /* 32Gb per CS */ + /* Single chip select */ + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 1, /* RTT_Wr = RZQ/4 */ + .rtt_nom = 2, /* RTT_Nom = RZQ/2 */ + .walat = 3, /* Write additional latency */ + .ralat = 7, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 0, /* Bank interleaving disabled */ + .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ +}; + +static struct mx6_ddr3_cfg novena_ddr_cfg = { + .mem_speed = 1600, + .density = 4, + .width = 64, + .banks = 8, + .rowaddr = 16, + .coladdr = 10, + .pagesz = 1, + .trcd = 1300, + .trcmin = 4900, + .trasmin = 3590, +}; + +#endif diff --git a/arch/arm/boards/novena/flash-header-novena.imxcfg b/arch/arm/boards/novena/flash-header-novena.imxcfg new file mode 100644 index 0000000000..0612542c19 --- /dev/null +++ b/arch/arm/boards/novena/flash-header-novena.imxcfg @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +loadaddr 0x00907000 +soc imx6 +max_load_size 0x11000 +ivtofs 0x400 diff --git a/arch/arm/boards/novena/lowlevel.c b/arch/arm/boards/novena/lowlevel.c new file mode 100644 index 0000000000..70aa92d5b4 --- /dev/null +++ b/arch/arm/boards/novena/lowlevel.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 John Watts <contact@jookia.org> + +#include <asm/barebox-arm.h> +#include <common.h> +#include <ddr_dimms.h> +#include <ddr_spd.h> +#include <debug_ll.h> +#include <mach/imx/debug_ll.h> +#include <mach/imx/esdctl.h> +#include <mach/imx/generic.h> +#include <mach/imx/imx6.h> +#include <mach/imx/imx6-mmdc.h> +#include <mach/imx/iomux-mx6.h> +#include <mach/imx/xload.h> +#include <pbl/i2c.h> +#include <soc/fsl/fsl_udc.h> +#include "ddr_regs.h" + +#define STACK_TOP (MX6_OCRAM_BASE_ADDR + MX6_OCRAM_MAX_SIZE) + +extern char __dtb_z_imx6q_novena_start[]; + +static struct spd_eeprom spd_eeprom; +static struct dimm_params dimm_params; + +static struct pbl_i2c *setup_spd_i2c(void) +{ + void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR); + void __iomem *i2c1base = IOMEM(MX6_I2C1_BASE_ADDR); + + imx_setup_pad(iomuxbase, MX6Q_PAD_CSI0_DAT8__I2C1_SDA); + imx_setup_pad(iomuxbase, MX6Q_PAD_CSI0_DAT9__I2C1_SCL); + + return imx6_i2c_early_init(i2c1base); +} + +static struct spd_eeprom *read_spd(void) +{ + struct spd_eeprom *eeprom = &spd_eeprom; + struct pbl_i2c *i2c = setup_spd_i2c(); + int rc; + + rc = spd_read_eeprom(i2c, 0x50, eeprom, SPD_MEMTYPE_DDR3); + if (rc < 0) { + pr_err("Couldn't read SPD EEPROM: %i\n", rc); + return NULL; + } + + rc = ddr3_spd_check(&eeprom->ddr3); + if (rc < 0) { + pr_err("Couldn't verify SPD data: %i\n", rc); + return NULL; + } + + return eeprom; +} + +static void setup_dimm_settings(struct dimm_params *params, + struct mx6_ddr_sysinfo *info, + struct mx6_ddr3_cfg *cfg) +{ + int capacity_gbit = params->capacity / 0x8000000; + int density_rank = capacity_gbit / params->n_ranks; + + info->ncs = params->n_ranks; + info->cs_density = density_rank; + cfg->mem_speed = params->tckmin_x_ps; + cfg->density = density_rank / params->n_banks_per_sdram_device; + cfg->width = params->data_width; + cfg->banks = params->n_banks_per_sdram_device; + cfg->rowaddr = params->n_row_addr; + cfg->coladdr = params->n_col_addr; + cfg->trcd = params->trcd_ps / 10; + cfg->trcmin = params->trc_ps / 10; + cfg->trasmin = params->tras_ps / 10; + cfg->SRT = params->extended_op_srt; + + if (params->device_width >= 16) + cfg->pagesz = 2; +} + +static void read_dimm_settings(void) +{ + struct spd_eeprom *eeprom = read_spd(); + struct dimm_params *params = &dimm_params; + int rc; + + if (!eeprom) { + pr_err("Couldn't read SPD EEPROM, using default settings\n"); + return; + } + + rc = ddr3_compute_dimm_parameters(&eeprom->ddr3, params); + if (rc < 0) { + pr_err("Couldn't compute DIMM params: %i\n", rc); + return; + } + + pr_info("Found DIMM: %s\n", params->mpart); + + if (params->primary_sdram_width != 64) { + pr_err("ERROR: DIMM stick memory width is not 64 bits\n"); + hang(); + } + + setup_dimm_settings(params, &novena_ddr_info, &novena_ddr_cfg); +} + +static bool running_from_ram(void) +{ + return (get_pc() >= MX6_MMDC_PORT01_BASE_ADDR); +} + +static void setup_uart(void) +{ + void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR); + void __iomem *uart2base = IOMEM(MX6_UART2_BASE_ADDR); + + /* NOTE: RX is needed for TX to work on this board */ + imx_setup_pad(iomuxbase, MX6Q_PAD_EIM_D26__UART2_RXD); + imx_setup_pad(iomuxbase, MX6Q_PAD_EIM_D27__UART2_TXD); + + imx6_uart_setup(uart2base); + pbl_set_putc(imx_uart_putc, uart2base); + + pr_debug(">"); +} + +static void setup_ram(void) +{ + read_dimm_settings(); + + mx6dq_dram_iocfg(64, &novena_ddr_regs, &novena_grp_regs); + mx6_dram_cfg(&novena_ddr_info, &novena_mmdc_calib, &novena_ddr_cfg); + + mmdc_do_write_level_calibration(); + mmdc_do_dqs_calibration(); +} + +static void load_barebox(void) +{ + enum bootsource bootsrc; + int bootinstance; + + imx6_get_boot_source(&bootsrc, &bootinstance); + + if (bootsrc == BOOTSOURCE_SERIAL) + imx6_barebox_start_usb(IOMEM(MX6_MMDC_PORT01_BASE_ADDR)); + else if (bootsrc == BOOTSOURCE_MMC) + imx6_esdhc_start_image(bootinstance); + + pr_err("Unsupported boot source %i instance %i\n", + bootsrc, bootinstance); + hang(); +} + +ENTRY_FUNCTION_WITHSTACK(start_imx6q_novena, STACK_TOP, r0, r1, r2) +{ + imx6_cpu_lowlevel_init(); + relocate_to_current_adr(); + setup_c(); + + imx6_ungate_all_peripherals(); + setup_uart(); + + if (!running_from_ram()) { + setup_ram(); + load_barebox(); + } else { + imx6q_barebox_entry(__dtb_z_imx6q_novena_start); + } +} |