/* * board.c * * Board functions for Netmodule NRHW 20, based on AM335x EVB * * Copyright (C) 2018-2020 NetModule AG - http://www.netmodule.com/ * Copyright (C) 2011, Texas Instruments, Incorporated - http://www.ti.com/ * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../common/bdparser.h" #include "../common/board_descriptor.h" #include "../common/da9063.h" #include "board.h" #include "shield.h" #include "shield_can.h" #include "shield_comio.h" #include "fileaccess.h" /* TODO: place in proper header file */ extern void serial_set_console_index(int index); extern int console_init_f(void); DECLARE_GLOBAL_DATA_PTR; /* * CPU GPIOs * * GPIO0_2: RST_GNSS~ * GPIO0_3: GEOFENCE_GNSS * GPIO0_4: RTK_STAT_GNSS * GPIO0_5: EXTINT_GNSS * GPIO0_6: TIMEPULSE_GNSS * * GPIO0_16: RST_PHY~ * GPIO0_17: PMIC FAULT * GPIO0_27: RST_SHIELD~ * GPIO0_31: GSM_WAKE * * GPIO1_14: DIG_OUT * GPIO1_15: DIG_IN * GPIO1_20: BT_EN * GPIO1_21: GSM_PWR_EN * GPIO1_25: RST_GSM * GPIO1_26: WLAN_EN * GPIO1_27: WLAN_IRQ * * GPIO3_0: BUTTON * GPIO3_4: PCIe_IO.WAKE * GPIO3_9: PCIe_IO.W_DIS * GPIO3_10: PCIe_IO.RST * GPIO3_17: SIM_SEL * GPIO3_21: RST_HUB~ (USB) */ #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio)) #define GPIO_RST_PHY_N GPIO_TO_PIN(0, 16) #define GPIO_RST_USB_HUB_N GPIO_TO_PIN(3, 21) #define GPIO_RST_GNSS GPIO_TO_PIN(0, 2) #define GPIO_RST_PCI GPIO_TO_PIN(3, 10) #define GPIO_RST_GSM GPIO_TO_PIN(1, 25) #define GPIO_PWR_GSM GPIO_TO_PIN(1, 21) #define GPIO_WAKE_GSM GPIO_TO_PIN(0, 31) #define GPIO_SIM_SEL GPIO_TO_PIN(3, 17) #define GPIO_WLAN_EN GPIO_TO_PIN(1, 26) #define GPIO_BT_EN GPIO_TO_PIN(1, 20) #define GPIO_DIG_OUT GPIO_TO_PIN(1, 14) #define GPIO_DIG_IN GPIO_TO_PIN(1, 15) #define GPIO_PCI_WDIS GPIO_TO_PIN(3, 9) /* * PMIC GPIOs * * GPIO_7: EN_SUPPLY_GSM * GPIO_8: VOLTAGE_SEL_PCIe * GPIO_9: EN_SUPPLY_PCIe * GPIO_10: LED0.RD~ * GPIO_11: LED0.GN~ */ #define PMIC_GSM_SUPPLY_EN_IO 7 #define PMIC_PCIe_SUPPLY_VSEL_IO 8 #define PMIC_PCIe_SUPPLY_EN_IO 9 #define PMIC_LED0_RED 10 #define PMIC_LED0_GREEN 11 /* * I2C IO Extender * * GPIO_0: LED0, Red * GPIO_1: LED0, Green * .. * GPIO_8: LED4, Red * GPIO_9: LED4, Green */ #define IOEXT_LED0_RED_MASK (1U << 0) #define IOEXT_LED0_GREEN_MASK (1U << 1) #define IOEXT_LED1_RED_MASK (1U << 2) #define IOEXT_LED1_GREEN_MASK (1U << 3) #define IOEXT_LED2_RED_MASK (1U << 4) #define IOEXT_LED2_GREEN_MASK (1U << 5) #define IOEXT_LED3_RED_MASK (1U << 6) #define IOEXT_LED3_GREEN_MASK (1U << 7) #define IOEXT_LED4_RED_MASK (1U << 8) #define IOEXT_LED4_GREEN_MASK (1U << 9) #define IOEXT_LEDS_ALL_MASK (0x03FF) #define NUM_INDICATOR_LEDS 5 #define DDR3_CLOCK_FREQUENCY (400) #if !defined(CONFIG_SPL_BUILD) /* Hardware version information of mainboard, loaded by get_hw_version() */ static int hw_ver = -1; static int hw_rev = -1; static int hw_patch = -1; static char hw_variant_name[64]; #endif #if !defined(CONFIG_SPL_BUILD) static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE; #endif #define I2C_BD_EEPROM_BUS (2) #define BD_EEPROM_ADDR (0x50) /* CPU BD EEPROM (8kByte) is at 50 (A0) */ #define BD_ADDRESS (0x0000) /* Board descriptor at beginning of EEPROM */ #define PD_ADDRESS (0x0200) /* Product descriptor */ #define PARTITION_ADDRESS (0x0600) /* Partition Table */ static BD_Context bdctx[3]; /* The descriptor contexts */ #if !defined(CONFIG_SPL_BUILD) static void request_and_set_gpio(int gpio, const char *name, int value) { int ret; ret = gpio_request(gpio, name); if (ret < 0) { printf("%s: Unable to request %s\n", __func__, name); return; } ret = gpio_direction_output(gpio, value); if (ret < 0) { printf("%s: Unable to set %s as output\n", __func__, name); goto err_free_gpio; } return; err_free_gpio: gpio_free(gpio); } #define REQUEST_AND_SET_GPIO(N) request_and_set_gpio(N, #N, 1); #define REQUEST_AND_CLEAR_GPIO(N) request_and_set_gpio(N, #N, 0); #endif static void init_leds(void) { int old_bus; old_bus = i2c_get_bus_num(); i2c_set_bus_num(CONFIG_SYS_I2C_PCA953X_BUS); /* Set all IOs as output */ (void)pca953x_set_dir(CONFIG_SYS_I2C_PCA953X_ADDR, IOEXT_LEDS_ALL_MASK, PCA953X_DIR_OUT); /* Set all LEDs off */ (void)pca953x_set_val(CONFIG_SYS_I2C_PCA953X_ADDR, IOEXT_LEDS_ALL_MASK, IOEXT_LEDS_ALL_MASK); i2c_set_bus_num(old_bus); } static void set_status_led(int red, int green) { int bus; /* LED outputs are active low, invert state */ bus = da9063_claim_i2c_bus(); (void)da9063_set_gpio(PMIC_LED0_RED, !red); (void)da9063_set_gpio(PMIC_LED0_GREEN, !green); da9063_release_i2c_bus(bus); } static void set_indicator_led(unsigned led, int red, int green) { int old_bus; uint led_red_mask = 0x1U << (2*led); uint led_green_mask = 0x2U << (2*led); uint led_val = 0; old_bus = i2c_get_bus_num(); i2c_set_bus_num(CONFIG_SYS_I2C_PCA953X_BUS); if (!red) led_val |= led_red_mask; if (!green) led_val |= led_green_mask; (void)pca953x_set_val(CONFIG_SYS_I2C_PCA953X_ADDR, led_red_mask | led_green_mask, led_val); i2c_set_bus_num(old_bus); } static void init_i2c(void) { i2c_set_bus_num(0); i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); i2c_set_bus_num(2); i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); i2c_set_bus_num(0); } static int _bd_init(void) { int old_bus; old_bus = i2c_get_bus_num(); i2c_set_bus_num(I2C_BD_EEPROM_BUS); if (bd_get_context(&bdctx[0], BD_EEPROM_ADDR, BD_ADDRESS) != 0) { printf("%s() no valid bd found\n", __func__); return -1; } if (bd_get_context(&bdctx[1], BD_EEPROM_ADDR, PD_ADDRESS) != 0) { printf("%s() no valid pd found\n", __func__); return -1; } if (bd_get_context(&bdctx[2], BD_EEPROM_ADDR, PARTITION_ADDRESS) != 0) { printf("%s() no valid partition table found\n", __func__); return -1; } bd_register_context_list(bdctx, ARRAY_SIZE(bdctx)); i2c_set_bus_num(old_bus); return 0; } static bool is_jtag_boot(uint32_t address) { char* jtag_token = (char*)address; if (strcmp(jtag_token, "JTAGBOOT") == 0) { strcpy(jtag_token, "jtagboot"); return true; } else { return false; } } /* * Read header information from EEPROM into global structure. */ static inline int __maybe_unused read_eeprom(void) { return _bd_init(); } /* * Selects console for SPL. * U-Boot console is selected in set_console() */ struct serial_device *default_serial_console(void) { /* * Mux pins for selected UART properly. * Note: UART indexes start at 0 while eserial indexes start at 1. * * Provide console on internal UART1 regardless of boot mode. * This only has a side effect when using X-Modem boot */ if (spl_boot_device() == BOOT_DEVICE_UART) { /* Continue booting from UART in case of serial (xmodem) boot */ enable_uart0_pin_mux(); return &eserial1_device; } else { /* Use bluetooth uart, if no ouput shall be seen. */ /* TODO: Sorry, what are we doing here.... */ /* This is at least dangerous if not completely wrong */ /* TODO: Using UART1 for moment until it is clear what to do */ #if 0 enable_uart5_pin_mux(); return &eserial6_device; #else enable_uart1_pin_mux(); return &eserial2_device; #endif } } #ifndef CONFIG_SKIP_LOWLEVEL_INIT static const struct ddr_data ddr3_data = { /* Ratios were optimized by DDR3 training software from TI */ .datardsratio0 = 0x39, .datawdsratio0 = 0x3f, .datafwsratio0 = 0x98, .datawrsratio0 = 0x7d, }; static const struct cmd_control ddr3_cmd_ctrl_data = { .cmd0csratio = MT41K256M16HA125E_RATIO, .cmd0iclkout = MT41K256M16HA125E_INVERT_CLKOUT, .cmd1csratio = MT41K256M16HA125E_RATIO, .cmd1iclkout = MT41K256M16HA125E_INVERT_CLKOUT, .cmd2csratio = MT41K256M16HA125E_RATIO, .cmd2iclkout = MT41K256M16HA125E_INVERT_CLKOUT, }; static struct emif_regs ddr3_emif_reg_data = { .sdram_config = MT41K256M16HA125E_EMIF_SDCFG, .ref_ctrl = 0x61A, /* 32ms > 85°C */ .sdram_tim1 = 0x0AAAE51B, .sdram_tim2 = 0x246B7FDA, .sdram_tim3 = 0x50FFE67F, .zq_config = MT41K256M16HA125E_ZQ_CFG, .emif_ddr_phy_ctlr_1 = MT41K256M16HA125E_EMIF_READ_LATENCY, }; #define OSC (V_OSCK/1000000) struct dpll_params dpll_ddr = { DDR3_CLOCK_FREQUENCY, OSC-1, 1, -1, -1, -1, -1 }; static void init_pmic_spl(void) { int bus; /* PMIC basic configuration */ da9063_init(CONFIG_PMIC_I2C_BUS); bus = da9063_claim_i2c_bus(); /* Configure default PMIC current limits. Will be overridden in Linux. * MEM = 1.5A (0.55A) * IO = 1.5A (0.5A) * PERI = 2.0A (1.0A) * PRO = 0.5A (unused) * CORE2 = 2.0A (0.55A) * CORE1 = 2.0A (0.25A, seems too low) */ (void)da9063_set_reg(PMIC_REG_BUCK_ILIM_A, 0x00); (void)da9063_set_reg(PMIC_REG_BUCK_ILIM_B, 0x50); (void)da9063_set_reg(PMIC_REG_BUCK_ILIM_C, 0xFF); /* * FB 57727 Use synchronous mode for buck converters * This solves the occasional rail lock up problem */ (void)da9063_set_reg(PMIC_REG_BCORE1_CONF, 0x81); (void)da9063_set_reg(PMIC_REG_BCORE2_CONF, 0x81); (void)da9063_set_reg(PMIC_REG_BIO_CONF, 0x81); (void)da9063_set_reg(PMIC_REG_BMEM_CONF, 0x81); (void)da9063_set_reg(PMIC_REG_BPERI_CONF, 0x81); /* Enable charging of RTC backup capacitor (1mA, 3.1V) */ (void)da9063_set_reg(PMIC_REG_BBAT_CONT, 0xAF); da9063_release_i2c_bus(bus); } struct reset_registers { uint32_t value; uint32_t value_crc; }; #ifdef CONFIG_NRSW_BUILD /* TODO: Move ethernet crc to dedicated file */ static uint32_t ether_crc(size_t len, uint8_t const *p) { uint32_t crc; unsigned i; crc = ~0; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); } /* an reverse the bits, cuz of way they arrive -- last-first */ crc = (crc >> 16) | (crc << 16); crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); return crc; } void check_pmic_reset_reason(unsigned int reset_reason_shm_location) { volatile struct reset_registers* reset_regs = (struct reset_registers*)reset_reason_shm_location; uint8_t state = 0x00; int bus; int ret; bus = da9063_claim_i2c_bus(); ret = da9063_get_reg(PMIC_REG_FAULT_LOG, &state); if ((ret == 0) && (state != 0)) { if (state & PMIC_FAULT_TWD_ERROR_MASK) { reset_regs->value = EXTERNAL_WATCHDOG_PATTERN; reset_regs->value_crc = ether_crc(sizeof(reset_regs->value), (const uint8_t*)&(reset_regs->value)); } /* clear pmic fault log by writing back all bits currently set */ da9063_set_reg(PMIC_REG_FAULT_LOG, state); } da9063_release_i2c_bus(bus); } #endif void am33xx_spl_board_init(void) { /* Set CPU speed to 600 MHz (fix) */ dpll_mpu_opp100.m = MPUPLL_M_600; /* Set CORE Frequencies to OPP100 (600MHz) */ do_setup_dpll(&dpll_core_regs, &dpll_core_opp100); /* Configure both I2C buses used */ init_i2c(); /* Setup PMIC */ init_pmic_spl(); init_leds(); #ifdef CONFIG_NRSW_BUILD set_indicator_led(0, 1, 1); /* Orange */ #else set_status_led(1, 0); /* Red */ set_indicator_led(0, 1, 0); /* Red */ #endif /* Set MPU Frequency to what we detected now that voltages are set */ do_setup_dpll(&dpll_mpu_regs, &dpll_mpu_opp100); #ifdef CONFIG_NRSW_BUILD check_pmic_reset_reason(RESET_REASON_SHM_LOCATION); #endif /* Debugger can place marker at end of SRAM to stop boot here */ if (is_jtag_boot(CONFIG_JTAG_MARKER_SPL)) { puts("Detected JTAG boot, executing bkpt #0\n"); __asm__ __volatile__ ("bkpt #0"); } } const struct dpll_params *get_dpll_ddr_params(void) { dpll_ddr.n = (get_osclk() / 1000000) - 1; return &dpll_ddr; } void set_uart_mux_conf(void) { enable_uart0_pin_mux(); enable_uart1_pin_mux(); } void set_mux_conf_regs(void) { enable_board_pin_mux(); } const struct ctrl_ioregs ioregs = { .cm0ioctl = MT41K256M16HA125E_IOCTRL_VALUE, .cm1ioctl = MT41K256M16HA125E_IOCTRL_VALUE, .cm2ioctl = MT41K256M16HA125E_IOCTRL_VALUE, .dt0ioctl = MT41K256M16HA125E_IOCTRL_VALUE, .dt1ioctl = MT41K256M16HA125E_IOCTRL_VALUE }; void sdram_init(void) { config_ddr(DDR3_CLOCK_FREQUENCY, &ioregs, &ddr3_data, &ddr3_cmd_ctrl_data, &ddr3_emif_reg_data, 0); } #endif /* CONFIG_SKIP_LOWLEVEL_INIT */ #if !defined(CONFIG_SPL_BUILD) /* * Override for Ethernet link timeout definition, * with option to specify via environment variable linktimeout */ int eth_phy_timeout(void) { const char* timeout_env = NULL; int timeout; timeout = PHY_ANEG_DEFAULT_TIMEOUT; /* * Check if timeout has been defined by environment. * Valid range: 1000..10000 milliseconds */ timeout_env = getenv("linktimeout"); if (timeout_env != NULL) { timeout = simple_strtoul(timeout_env, NULL, 10); if (timeout == 0) { timeout = PHY_ANEG_DEFAULT_TIMEOUT; } else if (timeout < 1000) { timeout = 1000; } else if (timeout > 10000) { timeout = 10000; } } return timeout; } #endif /* !defined(CONFIG_SPL_BUILD) */ #if !defined(CONFIG_SPL_BUILD) static void init_ethernet_switch(void) { REQUEST_AND_CLEAR_GPIO(GPIO_RST_PHY_N); mdelay(1); /* OMAP3 does not feature open drain pins, thus configure pin as input */ gpio_direction_input(GPIO_RST_PHY_N); /* When the Ethernet switch senses reset, it drives reset for 8..14ms * Wait longer than this time to avoid IO congestion later on. */ mdelay(20); } static void init_usb_hub(void) { REQUEST_AND_CLEAR_GPIO(GPIO_RST_USB_HUB_N); /* Minimum Reset Pulse = 1us */ mdelay(2); gpio_set_value(GPIO_RST_USB_HUB_N, 1); } static void init_pcie_slot(void) { int bus; puts("PCIe: "); bus = da9063_claim_i2c_bus(); REQUEST_AND_SET_GPIO(GPIO_RST_PCI); /* Assert reset (active high) */ da9063_set_gpio(PMIC_PCIe_SUPPLY_EN_IO, 0); /* Switch PCIe Supply off */ mdelay(30); /* Give time to discharge output */ da9063_set_gpio(PMIC_PCIe_SUPPLY_VSEL_IO, 0); /* Set voltage to 3.3V */ mdelay(1); da9063_set_gpio(PMIC_PCIe_SUPPLY_EN_IO, 1); /* Enable Supply */ mdelay(10); /* PCIe requires at least 1ms delay between power on and reset release */ gpio_set_value(GPIO_RST_PCI, 0); /* Release reset */ REQUEST_AND_CLEAR_GPIO(GPIO_PCI_WDIS); /* Allow wireless operation */ da9063_release_i2c_bus(bus); puts("ready\n"); } static void init_gsm(void) { /* * Perform power up sequence for TOBY-L2 modem. * * TOBY-L2 series can be switched on in one of the following ways: * - Rising edge on the VCC pin to a valid voltage for module supply, * i.e. applying module supply * - Low level on the PWR_ON pin, which is normally set high by an * internal pull-up, for a valid time period when the applied VCC * voltage is within the valid operating range (see section 4.2.8). * - Low level on the RESET_N pin, which is normally set high by an * internal pull-up, for a valid time period when the applied VCC * voltage is within the valid operating range (see section 4.2.9). * * PWR_ON low time: 5 ms - Low time to switch-on the module * RESET_N low time: 18..800 ms - Low time to switch-on the module * 2.1..15 s - Low time to reset the module * 16 s - Low time to switch-off the module * * References: * - uBlox TOBY-L2 Datasheet UBX-13004573 - R24 * 2.3.1 Module power-on * 4.2.8 PWR_ON pin * 4.2.9 RESET_N pin * */ int bus; puts("GSM: "); bus = da9063_claim_i2c_bus(); /* TODO: Keep Power-On and use GSM Modem Reset Signal to restart */ REQUEST_AND_SET_GPIO(GPIO_RST_GSM); /* Assert reset (active high) */ REQUEST_AND_CLEAR_GPIO(GPIO_PWR_GSM); /* Keep power switch inactive (released) */ da9063_set_gpio(PMIC_GSM_SUPPLY_EN_IO, 0); /* Switch GSM Supply off */ mdelay(30+100); /* Give time to discharge supply */ /* Keep of for 100ms, #3.3.2 */ da9063_set_gpio(PMIC_GSM_SUPPLY_EN_IO, 1); /* Enable GSM supply */ mdelay(10); gpio_set_value(GPIO_RST_GSM, 0); /* Take modem out of reset */ mdelay(300); /* Wait for power to stabilizy, #3.4.2 */ gpio_set_value(GPIO_PWR_GSM, 1); /* Generate power on event, #3.4.2 */ mdelay(1200); gpio_set_value(GPIO_PWR_GSM, 0); da9063_release_i2c_bus(bus); puts("ready\n"); } static void init_gnss(void) { /* * Release GNSS reset line, so that module starts up early */ REQUEST_AND_SET_GPIO(GPIO_RST_GNSS); } #endif /* !defined(CONFIG_SPL_BUILD) */ /* * Basic board specific setup. Pinmux has been handled already. * Not called in SPL build. */ int board_init(void) { #if defined(CONFIG_HW_WATCHDOG) hw_watchdog_init(); #endif gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; /* Configure both I2C buses used */ init_i2c(); da9063_init(CONFIG_PMIC_I2C_BUS); /* Let user know we're starting */ init_leds(); #ifdef CONFIG_NRSW_BUILD set_status_led(1, 1); /* Orange */ #else set_status_led(1, 1); /* Orange */ set_indicator_led(0, 0, 0); /* Off */ #endif printf("OSC: %lu MHz\n", get_osclk()/1000000); return 0; } #if !defined(CONFIG_SPL_BUILD) /* * Set Linux console based on * - Selection in /root/boot/consoledev * - Available tty interfaces * - ttyS1: standard console (default) * - ttyS0: COM/IO shield * - ttyNull0: Dummy device if no real UART is available */ void set_console(void) { const char *defaultconsole = getenv("defaultconsole"); char buf[20]; int i; /* Set default console to ttyS1 if not yet defined in env */ if (defaultconsole == 0) { setenv("defaultconsole", "ttyS1"); } /* If consoledev file is present, take the tty defined in it as console */ if (read_file("/root/boot/consoledev",buf, sizeof(buf)) > 3) { if (strstr(buf, "tty") == buf) { buf[sizeof(buf)-1] = 0; for (i=0; i 1) { boot_partition = 0; } /* mmcblk1p1 => root0, mmcblk1p2 => root1 so +1 */ setenv_ulong("root_part", boot_partition + 1); } static void get_variant_name(void) { bd_get_variantname(hw_variant_name, sizeof(hw_variant_name)); printf("SYS: %s\n", hw_variant_name); } static void get_hw_version(void) { #ifdef CONFIG_NRSW_BUILD char hw_versions[16]; char new_env[256]; /* current bootargs = 84 bytes */ #endif bd_get_hw_version(&hw_ver, &hw_rev); bd_get_hw_patch(&hw_patch); printf("HW20: V%d.%d\n", hw_ver, hw_rev); #ifdef CONFIG_NRSW_BUILD /* add hardware versions to environment */ snprintf(hw_versions, sizeof(hw_versions), "CP=%d.%d", hw_ver, hw_rev); snprintf(new_env, sizeof(new_env), "setenv bootargs $bootargs %s", hw_versions); setenv("add_version_bootargs", new_env); #endif } static void get_pmic_version(void) { uint8_t val = 0x00; uint8_t ver, rev; int bus; int rc; bus = da9063_claim_i2c_bus(); rc = da9063_get_reg(PMIC_REG_CONFIG_ID, &val); if (!rc) { ver = (val >> 4) & 0xF; rev = (val >> 0) & 0xF; } else { ver = 0; rev = 0; } da9063_release_i2c_bus(bus); printf("PMIC: V%d.%d\n", ver, rev); } static void check_jtag_boot(void) { if (is_jtag_boot(CONFIG_JTAG_MARKER_UBOOT)) { char *bootcmd = getenv("bootcmd"); setenv ("bootcmd", ""); /* Save original bootcmd in "bootcmd_orig" to allow manual boot */ setenv ("bootcmd_orig", bootcmd); puts("Detected JTAG boot. Waiting on command line\n"); } } static void check_fct(void) { /* * Check whether an I2C device (EEPROM) is present at address 0xA2/0x51 * In this case we are connected to the factory test station. * Clear the bootcmd, so that test system can easily connect. */ int old_bus; old_bus = i2c_get_bus_num(); i2c_set_bus_num(I2C_BD_EEPROM_BUS); /* If probe fails we are sure no eeprom is connected */ if (i2c_probe(0x51) == 0) { setenv ("bootcmd", ""); puts("Detected factory test system. Waiting on command line\n"); } i2c_set_bus_num(old_bus); } struct shield_command { int shield_id; const char *name; const char *default_shieldcmd; void (*init)(void); }; static struct shield_command known_shield_commands[] = { { SHIELD_COM_IO, "comio", "shield comio mode rs232", comio_shield_init }, { SHIELD_DUALCAN, "dualcan", "shield dualcan termination off off", can_shield_init }, { SHIELD_DUALCAN_PASSIVE, "dualcan-passive", "shield dualcan-passive", can_shield_passive_init } }; static const struct shield_command* get_shield_command(int shield_id) { int i; for (i = 0; i < ARRAY_SIZE(known_shield_commands); i++) { if (known_shield_commands[i].shield_id == shield_id) { return &known_shield_commands[i]; } } return NULL; } static void shield_config(void) { #define MAX_SHIELD_CMD_LEN 128 char shieldcmd_linux[MAX_SHIELD_CMD_LEN]; const char *shieldcmd = ";"; /* default shield command is empty */ const struct shield_command *cmd; int len; int shield_id = bd_get_shield(0); if (shield_id < 0) { debug("No shield found in bd\n"); goto end; } cmd = get_shield_command(shield_id); if (cmd == NULL) { printf ("Unknown shield id %d\n", shield_id); goto end; } printf("Shield:%s\n", cmd->name); cmd->init(); shieldcmd = cmd->default_shieldcmd; /* If a shield configuration is set by Linux, take it without bd check. * We asume that Linux knows what to do. */ len = read_file("/root/boot/shieldcmd", shieldcmd_linux, MAX_SHIELD_CMD_LEN); if (len > 0) { debug("Shield command found in file, using it\n"); shieldcmd = shieldcmd_linux; } end: setenv("shieldcmd", shieldcmd); } static void shield_init(void) { shield_config(); } static bool get_button_state(void) { uint8_t state = 0x00; bool pressed = false; int bus; int rc; bus = da9063_claim_i2c_bus(); rc = da9063_get_reg(PMIC_REG_STATUS_A, &state); da9063_release_i2c_bus(bus); if (!rc) { pressed = (state & 0x01) == 0x01; } return pressed; } static void blink_led(int red, int green) { const int pulse_width = 400*1000; /* 400ms */ int i; /* Assumes status led is on (green), indicator off */ /* udelay(pulse_width); */ set_status_led(red, green); for (i=0; i 12s */ puts("Booting recovery image...\n"); /* Set consoledev to external port */ setenv("defaultconsole", "ttyS1"); /* Set bootcmd to run recovery */ setenv("bootcmd", "run recovery"); } } #endif /* !defined(CONFIG_SPL_BUILD) */ int board_late_init(void) { #if !defined(CONFIG_SPL_BUILD) if (read_eeprom() < 0) { puts("Could not get board ID.\n"); } get_variant_name(); get_hw_version(); get_pmic_version(); set_root_partition(); set_devicetree_name(); /* Initialize pins */ REQUEST_AND_CLEAR_GPIO(GPIO_WLAN_EN); REQUEST_AND_CLEAR_GPIO(GPIO_BT_EN); REQUEST_AND_CLEAR_GPIO(GPIO_DIG_OUT); init_ethernet_switch(); init_usb_hub(); init_pcie_slot(); init_gsm(); init_gnss(); /* * Check if a user action is requested * - Short press: factory reset * - Long press: recovery boot */ check_reset_button(); set_console(); shield_init(); set_status_led(0, 0); /* WAN led off */ set_indicator_led(0, 0, 1); /* Green */ check_fct(); check_jtag_boot(); #endif return 0; } #ifndef CONFIG_DM_ETH #if (defined(CONFIG_DRIVER_TI_CPSW) && !defined(CONFIG_SPL_BUILD)) || \ (defined(CONFIG_SPL_ETH_SUPPORT) && defined(CONFIG_SPL_BUILD)) static void cpsw_control(int enabled) { /* VTP can be added here */ return; } static struct cpsw_slave_data cpsw_slaves[] = { { .slave_reg_ofs = 0x208, .sliver_reg_ofs = 0xd80, .phy_if = PHY_INTERFACE_MODE_RMII, .phy_addr = 0 } }; static struct cpsw_platform_data cpsw_data = { .mdio_base = CPSW_MDIO_BASE, .cpsw_base = CPSW_BASE, .mdio_div = 0xff, .channels = 8, .cpdma_reg_ofs = 0x800, .slaves = 1, .slave_data = cpsw_slaves, .ale_reg_ofs = 0xd00, .ale_entries = 1024, .host_port_reg_ofs = 0x108, .hw_stats_reg_ofs = 0x900, .bd_ram_ofs = 0x2000, .mac_control = (1 << 5), .control = cpsw_control, .host_port_num = 0, .version = CPSW_CTRL_VERSION_2, }; #endif #if ((defined(CONFIG_SPL_ETH_SUPPORT) || defined(CONFIG_SPL_USBETH_SUPPORT)) &&\ defined(CONFIG_SPL_BUILD)) || \ ((defined(CONFIG_DRIVER_TI_CPSW) || \ defined(CONFIG_USB_ETHER) && defined(CONFIG_MUSB_GADGET)) && \ !defined(CONFIG_SPL_BUILD)) static void set_mac_address(int index, uchar mac[6]) { /* Then take mac from bd */ if (is_valid_ethaddr(mac)) { eth_setenv_enetaddr_by_index("eth", index, mac); } else { printf("Trying to set invalid MAC address"); } } /* TODO: Update doc */ /* * This function will: * Read the eFuse for MAC addresses, and set ethaddr/eth1addr/usbnet_devaddr * in the environment * Perform fixups to the PHY present on certain boards. We only need this * function in: * - SPL with either CPSW or USB ethernet support * - Full U-Boot, with either CPSW or USB ethernet * Build in only these cases to avoid warnings about unused variables * when we build an SPL that has neither option but full U-Boot will. */ int board_eth_init(bd_t *bis) { int n = 0; __maybe_unused uint8_t mac_addr0[6] = {02,00,00,00,00,01}; #if !defined(CONFIG_SPL_BUILD) #ifdef CONFIG_DRIVER_TI_CPSW cpsw_data.mdio_div = 0x3E; bd_get_mac(0, mac_addr0, sizeof(mac_addr0)); set_mac_address(0, mac_addr0); writel(RMII_MODE_ENABLE | RMII_CHIPCKL_ENABLE, &cdev->miisel); { int rv = cpsw_register(&cpsw_data); if (rv < 0) { printf("Error %d registering CPSW switch\n", rv); } else { n += rv; } } #endif #endif #if defined(CONFIG_USB_ETHER) && \ (!defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_USBETH_SUPPORT)) if (is_valid_ethaddr(mac_addr0)) { eth_setenv_enetaddr("usbnet_devaddr", mac_addr0); } { int rv = usb_eth_initialize(bis); if (rv < 0) { printf("Error %d registering USB_ETHER\n", rv); } else { n += rv; } } #endif return n; } #endif #endif /* CONFIG_DM_ETH */ #ifdef CONFIG_SPL_LOAD_FIT int board_fit_config_name_match(const char *name) { return 0; } #endif #if defined(CONFIG_OF_BOARD_SETUP) && !defined(CONFIG_SPL_BUILD) static void ft_enable_node(void* blob, const char* name) { int node_ofs = -1; node_ofs = fdt_path_offset(blob, name); if (node_ofs >= 0) { fdt_setprop_string(blob, node_ofs, "status", "okay"); } } /* * Modify the name of a gpio in a gpio-line-names string list. */ static void ft_set_gpio_name(void *blob, const char* gpio, int pin, const char* name) { int node_ofs = fdt_path_offset(blob, gpio); if (node_ofs != -1) { const struct fdt_property* prop = fdt_get_property(blob, node_ofs, "gpio-line-names", NULL); if (prop != NULL) { const char* text; int pos = 0; int i; char buffer[512]; /* TODO: Determine number of entries from ngpios field */ for (i=0; i<32; i++) { if (i == pin) { /* Take provided name if GPIO pin is matched */ text = name; } else { /* Take existing name from strin list */ (void)fdt_get_string_index(blob, node_ofs, "gpio-line-names", i, &text); } /* Add name to new string list */ strncpy(buffer+pos, text, sizeof(buffer)-pos); pos += strlen(text) + 1; } (void)fdt_setprop(blob, node_ofs, "gpio-line-names", buffer, pos); } } } /* * Enable digital IOs provided by COM/IO shield in gpio nodes */ static void ft_comio_gpios(void *blob) { /* gpio0_7: COM/IO relay output */ ft_set_gpio_name(blob, "/ocp/gpio@44e07000", 7, "COMIO_OUT0"); /* gpio1_8: COM/IO digital input */ ft_set_gpio_name(blob, "/ocp/gpio@4804c000", 8, "COMIO_IN0"); } static void ft_shields(void* blob) { int shield_type = -1; shield_type = bd_get_shield(0); switch (shield_type) { case SHIELD_COM_IO: ft_comio_gpios(blob); ft_enable_node(blob, "/netbox_dio_comio"); /* TODO: Should use alias serial0 */ ft_enable_node(blob, "/ocp/serial@44e09000"); break; case SHIELD_DUALCAN: case SHIELD_DUALCAN_PASSIVE: /* TODO: Should use alias d_can0, d_can1 */ ft_enable_node(blob, "/ocp/can@481cc000"); ft_enable_node(blob, "/ocp/can@481d0000"); break; default: ft_enable_node(blob, "/netbox_dio_default"); break; }; } static void ft_bootloader_version(void *blob) { int node_offset; static const char version_string[] = U_BOOT_VERSION_STRING; node_offset = fdt_path_offset(blob, "/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "nm,bootloader,version", version_string); } } static void ft_hw_info(void *blob) { int node_offset; char hw_version[16]; snprintf(hw_version, sizeof(hw_version), "%d.%d.%d", hw_ver, hw_rev, hw_patch); node_offset = fdt_path_offset(blob, "/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "model", hw_variant_name); fdt_setprop_string(blob, node_offset, "nm,carrierboard,version", hw_version); } } int ft_board_setup(void *blob, bd_t *bd) { ft_bootloader_version(blob); ft_hw_info(blob); ft_shields(blob); return 0; } #endif /* defined(CONFIG_OF_BOARD_SETUP) && !defined(CONFIG_SPL_BUILD) */