/* * board.c * * Board functions for Netmodule NRHW 24, 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 * * (A17) GPIO0_2: RST_GNSS~ * (A16) GPIO0_5: EXTINT_GNSS * (C15) GPIO0_6: TIMEPULSE_GNSS * * (J18) GPIO0_16: RST_PHY~ * (U12) GPIO0_27: RST_SHIELD~ * * (R14) GPIO1_20: BT_EN * (V15) GPIO1_21: GSM_PWR_EN * (U16) GPIO1_25: RST_GSM * (T16) GPIO1_26: WLAN_EN * (V17) GPIO1_27: WLAN_IRQ * * (C12) GPIO3_17: SIM_SEL */ #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio)) #define GPIO_RST_GSM GPIO_TO_PIN(1, 25) #define GPIO_PWR_GSM GPIO_TO_PIN(1, 21) #define GPIO_WLAN_EN GPIO_TO_PIN(1, 26) #define GPIO_BT_EN GPIO_TO_PIN(1, 20) #define GPIO_RST_GNSS GPIO_TO_PIN(0, 2) #define GPIO_RST_ETH_N GPIO_TO_PIN(0, 16) #define GPIO_SIM_SEL GPIO_TO_PIN(3, 17) /* * PMIC GPIOs * * GPIO_7: EN_SUPPLY_GSM * GPIO_8: EN_SUPPLY_WIFI * GPIO_10: LED.LWR * GPIO_11: LED.UPR */ #define PMIC_GSM_SUPPLY_EN_IO 7 #define PMIC_WIFI_SUPPLY_EN_IO 8 #define PMIC_LED0 10 /* Lower */ #define PMIC_LED1 11 /* Upper */ #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) { /* No init code required */ } static void set_status_led(int red, int green) { int bus; /* * Note: Only green color LED is available in hw25 * Enable LED when either red or green is desired. */ bus = da9063_claim_i2c_bus(); da9063_set_gpio(PMIC_LED1, red | green); da9063_release_i2c_bus(bus); } static void set_indicator_led(int red, int green) { int bus; /* See above */ bus = da9063_claim_i2c_bus(); da9063_set_gpio(PMIC_LED0, red | green); da9063_release_i2c_bus(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 { /* Regular and JTAG boot use internal UART1 */ enable_uart1_pin_mux(); return &eserial2_device; } } #ifndef CONFIG_SKIP_LOWLEVEL_INIT static const struct ddr_data ddr3_data = { /* Ratios were optimized by DDR3 training software from TI */ .datardsratio0 = 0x39, /* 0x39 */ .datawdsratio0 = 0x3f, /* 0x40 */ /* 3f */ .datafwsratio0 = 0x98, /* 0x96 */ /* 98 */ .datawrsratio0 = 0x7d, /* 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 pmic_ignition_gate_on(void) { uint8_t val; /* Configure GPIO15 to permanent high, so that ignition sense signal is readable */ (void)da9063_set_reg(PMIC_REG_GPIO14_15, 0xCC); /* GPIO14/15 = Outputs open drain */ (void)da9063_get_reg(PMIC_REG_CONFIG_L, &val); /* Enable pull ups on GPIO14/15 */ val |= 0xC0; (void)da9063_set_reg(PMIC_REG_CONFIG_L, val); (void)da9063_get_reg(PMIC_REG_CONTROL_D, &val); /* No blinking, state selected by GPIOxx_MODE */ val &= ~0xF8; (void)da9063_set_reg(PMIC_REG_CONTROL_D, val); (void)da9063_get_reg(PMIC_REG_GPIO_MODE8_15, &val); /* Set to GPIO14,15 to high */ val |= 0xC0; (void)da9063_set_reg(PMIC_REG_GPIO_MODE8_15, val); } static void init_pmic_spl(void) { int bus; /* PMIC basic configuration */ da9063_init(CONFIG_PMIC_I2C_BUS); bus = da9063_claim_i2c_bus(); /* Enable +3V3_GNSS (LDO6) */ (void)da9063_set_reg(PMIC_REG_LDO6_CONT, PMIC_LDOx_EN_MASK); mdelay(2); pmic_ignition_gate_on(); /* 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(); #ifndef CONFIG_NRSW_BUILD set_status_led(1, 0); /* Red */ set_indicator_led(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(void) { REQUEST_AND_CLEAR_GPIO(GPIO_RST_ETH_N); /* Minimum Reset Pulse = 100us (SMSC8720) */ mdelay(1); gpio_set_value(GPIO_RST_ETH_N, 1); /* Give clocks time to stabilize */ mdelay(1); } static void init_sim_mux(void) { /* * Switch pluggable micro SIM to onboard modem (mux = 0) */ REQUEST_AND_CLEAR_GPIO(GPIO_SIM_SEL); } 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 * * Functionality Yocto: * - Leave GSM power enable as is (default at power up = off) * - Set reset line inactive (note: inverter logic in HW present) * - Leave button unpressed (note: inverter logic in HW present) * - Modem shall be enabled by Linux system by enabling GSM power * supply */ #ifdef CONFIG_NRSW_BUILD 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"); #else puts("GSM: "); REQUEST_AND_CLEAR_GPIO(GPIO_RST_GSM); /* Set reset inactive (active high) */ REQUEST_AND_CLEAR_GPIO(GPIO_PWR_GSM); /* Set power switch inactive/released (active high) */ puts("init\n"); #endif } static void init_gnss(void) { /* * Release GNSS reset line, so that module starts up early */ REQUEST_AND_SET_GPIO(GPIO_RST_GNSS); } /* TODO: Double Check - WiFi is enabled by sequencer already */ static void init_wifi(void) { int bus; /* Enable WiFi power supply */ bus = da9063_claim_i2c_bus(); da9063_set_gpio(PMIC_WIFI_SUPPLY_EN_IO, 1); da9063_release_i2c_bus(bus); } #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(); set_status_led(1, 1); /* Orange */ set_indicator_led(0, 0); /* Off */ 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, internal only) * - ttyS0: COM/IO shield (or used as console by kernel, * when no other console available) * - ttyNull0: Dummy device if no real UART is available */ void set_console(void) { const char *defaultconsole = getenv("defaultconsole"); int shield_id = bd_get_shield(0); /* Set default console to ttyS1 if not yet defined in env */ if (defaultconsole == 0) { setenv("defaultconsole", "ttyS1"); } /* * Always use internal console for u-boot * as COM/IO shield is not ready at that time. * (Needs to be initialized first using the * shieldcmd that is run by bootcmd.) */ serial_set_console_index(1); #if defined(CONFIG_PRE_CONSOLE_BUFFER) serial_init(); /* serial communications setup */ console_init_f(); /* stage 1 init of console */ #endif if (shield_id == SHIELD_COM_IO) { char buf[20]; /* * With COM/IO shield the defaultconsole for the kernel should * be ttyS0 (external port). * If consoledev file is present, take the tty defined in it as console */ setenv("defaultconsole", "ttyS0"); if (read_file("/root/boot/consoledev", buf, sizeof(buf)) > 3) { if (strstr(buf, "tty") == buf) { int i; /* TODO: What is this code doing? */ /* Truncating after whitespace? */ /* are there broken consoledev files around ? */ 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("HW25: 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 pulses) { const int pulse_width = 400*1000; /* 400ms */ /* Assumes status led on, indicator off */ set_status_led(0, 0); while (pulses) { udelay(pulse_width); set_status_led(1, 1); set_indicator_led(1, 1); udelay(pulse_width); set_status_led(0, 0); set_indicator_led(0, 0); pulses--; } udelay(pulse_width); set_status_led(1, 1); /* Orange */ } static void check_reset_button(void) { int counter = 0; /* Check how long button is pressed */ do { if (!get_button_state()) break; udelay(100*1000); /* 100ms */ counter += 100; if (counter == 2000) { /* Indicate factory reset threshold */ blink_led(1); } else if (counter == 12000) { /* Indicate recovery boot threshold */ blink_led(2); } } while (counter < 12000); if (counter < 2000) { /* Don't do anything for duration < 2s */ } else if (counter < 12000) { /* Do factory reset for duration between 2s and 12s */ char new_bootargs[512]; char *bootargs = getenv("bootargs"); if (bootargs == 0) bootargs = ""; puts("Do factory reset during boot...\n"); strncpy(new_bootargs, bootargs, sizeof(new_bootargs)); strncat(new_bootargs, " factory-reset", sizeof(new_bootargs)); setenv("bootargs", new_bootargs); } else { /* Boot into recovery for duration > 12s */ puts("Booting recovery image...\n"); /* TODO: ... internal .. port on HW25 */ /* 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); init_ethernet(); init_sim_mux(); init_gsm(); init_gnss(); init_wifi(); /* * Check if a user action is requested * - Short press: factory reset * - Long press: recovery boot */ check_reset_button(); set_console(); shield_init(); 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); int gpios = -1; const char* text; int pos = 0; int i; char buffer[512]; if (node_ofs == -1) { printf("Can't find node %s\n", gpio); goto end; } /* get number of IOs in node */ gpios = fdt_getprop_u32_default_node(blob, node_ofs, 0, "ngpios", -1); if (gpios == -1 || gpios > 64) { printf("Illegal number of gpios %d\n", gpios); goto end; } /* get string array with names */ const struct fdt_property* prop = fdt_get_property(blob, node_ofs, "gpio-line-names", NULL); if (prop == NULL) { goto end; } /* modify given name */ for (i=0; i 120) { temp_in_degs = 120; } printf("WARNING: Overriding CPU thermal alert to %d°C, critical to 125°C\n", temp_in_degs); node_ofs = fdt_path_offset(blob, "/thermal-zones/cpu-thermal/trips/cpu-alert0"); if (node_ofs >= 0) { fdt_setprop_inplace_u32(blob, node_ofs, "temperature", temp_in_degs*1000); } node_ofs = fdt_path_offset(blob, "/thermal-zones/cpu-thermal/trips/cpu-crit"); if (node_ofs >= 0) { fdt_setprop_inplace_u32(blob, node_ofs, "temperature", 125*1000); } } } int ft_board_setup(void *blob, bd_t *bd) { ft_bootloader_version(blob); ft_hw_info(blob); ft_shields(blob); ft_override_thermal(blob); return 0; } #endif /* defined(CONFIG_OF_BOARD_SETUP) && !defined(CONFIG_SPL_BUILD) */