/* * board.c * * Board functions for Netmodule nmhw21 board, based on AM335x EVB * * Copyright (C) 2018-2019 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 #include #include #include "../common/bdparser.h" #include "../common/board_descriptor.h" #include "../common/da9063.h" #include "board.h" #include "sja1105.h" #include "ui.h" #include "um.h" DECLARE_GLOBAL_DATA_PTR; /* * CPU GPIOs * * (J18) GPIO0_16: ETH_SW_RST~ (V2.0) * (K15) GPIO0_17: CTRL.INT~ * (T10) GPIO0_23: CAN_TERM1~ (V1.0) * (T17) GPIO0_30: LED0.RD * * (T12) GPIO1_12: SIM_SW * (V13) GPIO1_14: GNSS_RST~ * (U13) GPIO1_15: CAN_TERM0~ (V1.0) * (R14) GPIO1_20: BT_EN * (V15) GPIO1_21: GSM_PWR_EN * (U15) GPIO1_22: LED1.RD * (V16) GPIO1_24: LED1.GN * (U16) GPIO1_25: RST_GSM * (T16) GPIO1_26: WLAN_EN * (V17) GPIO1_27: WLAN_IRQ * (U18) GPIO1_28: LED0.GN * * (U3) GPIO2_16: TIMEPULSE~ (HW26) * (R6) GPIO2_25: RST_ETH~ * * (J17) GPIO3_4: GNSS_EXTINT * (K18) GPIO3_9: CTRL.W_DIS * (L18) GPIO3_10: CTRL.RST * (C12) GPIO3_17: UI_RST~ * (A14) GPIO3_21: RST_HUB~ (USB) */ #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio)) #define GPIO_LED0_GREEN GPIO_TO_PIN(0, 30) #define GPIO_LED0_RED GPIO_TO_PIN(1, 28) #define GPIO_LED1_GREEN GPIO_TO_PIN(1, 24) #define GPIO_LED1_RED GPIO_TO_PIN(1, 22) #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(1, 14) #define GPIO_RST_ETH_SW_N GPIO_TO_PIN(0, 16) #define GPIO_RST_ETH_N GPIO_TO_PIN(2, 25) #define GPIO_RST_USB_HUB_N GPIO_TO_PIN(3, 21) #define GPIO_RST_UM_N GPIO_TO_PIN(3, 10) #define GPIO_CTRL_WDIS_N GPIO_TO_PIN(3, 9) #define GPIO_CTRL_INT_N GPIO_TO_PIN(0, 17) #define GPIO_RST_UI_N GPIO_TO_PIN(3, 17) #define CAN0_TERM_N GPIO_TO_PIN(1, 15) #define CAN1_TERM_N GPIO_TO_PIN(0, 23) #define GPIO_SIM_SEL GPIO_TO_PIN(1, 12) #define GPIO_TIMEPULSE GPIO_TO_PIN(2, 16) #define GPIO_UART2_RX GPIO_TO_PIN(0, 2) /* UART Rx Pin as GPIO */ /* * PMIC GPIOs * * GPIO_5: EN_SUPPLY_GSM * GPIO_8: VOLTAGE_SEL_UM * GPIO_9: EN_SUPPLY_UM */ #define PMIC_GSM_SUPPLY_EN_IO 5 #define PMIC_UM_SUPPLY_VSEL_IO 8 #define PMIC_UM_SUPPLY_EN_IO 9 /* * I2C IO Extender (On UI Interface) */ #define IOEXT_LED1_RED_MASK (1U << 7) #define IOEXT_LED1_GREEN_MASK (1U << 6) #define IOEXT_LED2_RED_MASK (1U << 8) #define IOEXT_LED2_GREEN_MASK (1U << 9) #define IOEXT_LEDS_ALL_MASK (0x03C0) #define DDR3_CLOCK_FREQUENCY (400) #define DURATION_FACTORY_RESET (2000) /* Reset button time to initiate factory reset */ #define DURATION_RECOVERY_BOOT (12000) /* Reset button time to initiate recovery boot */ #if (DURATION_RECOVERY_BOOT < (DURATION_FACTORY_RESET+1000)) #error Recovery boot time must be larger than factory reset + 1 second #endif #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 int hw_type = -1; static char hw_variant_name[64]; #else static int hw_type = -1; #endif #if !defined(CONFIG_SPL_BUILD) static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE; #endif #define I2C_BD_EEPROM_BUS (1) #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 */ #define CONFIG_OF_BOARD_SETUP #if !defined(CONFIG_SPL_BUILD) /* SPI1 for Ethernet switch configuration */ static struct module_pin_mux spi1_pin_mux[] = { {OFFSET(ecap0_in_pwm0_out), (MODE(4) | PULLUDEN | PULLDOWN_EN | RXACTIVE)}, /* (C18) spi1_clk */ {OFFSET(uart0_rtsn), (MODE(4) | PULLUDEN | PULLUP_EN)}, /* (E17) spi1_mosi */ {OFFSET(uart0_ctsn), (MODE(4) | PULLUDEN | PULLUP_EN | RXACTIVE)}, /* (E18) spi1_miso */ {OFFSET(uart0_txd), (MODE(1) | PULLUDEN | PULLUP_EN)}, /* (E16) spi1_cs1 */ {-1} }; static struct mii_dev olddev; /* Backup of PHY driver structure for read function override */ #endif 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); static void init_leds(void) { REQUEST_AND_SET_GPIO(GPIO_LED0_RED); REQUEST_AND_SET_GPIO(GPIO_LED0_GREEN); REQUEST_AND_SET_GPIO(GPIO_LED1_RED); REQUEST_AND_SET_GPIO(GPIO_LED1_GREEN); } static void set_status_led(int red, int green) { gpio_set_value(GPIO_LED0_RED, red); gpio_set_value(GPIO_LED0_GREEN, green); } static void set_indicator_led(int red, int green) { gpio_set_value(GPIO_LED1_RED, red); gpio_set_value(GPIO_LED1_GREEN, green); } #ifndef CONFIG_NRSW_BUILD static void ui_set_status_led(int red, int green) { ui_set_top_led(red, green); } static void ui_set_indicator_led(int red, int green) { ui_set_bottom_led(red, green); } #endif static void init_i2c(void) { i2c_set_bus_num(0); i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); i2c_set_bus_num(1); 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; } #ifdef CONFIG_NRSW_BUILD /* TODO: Check if we can get rid of this descriptor and use eMMC partition table */ if (bd_get_context(&bdctx[2], BD_EEPROM_ADDR, PARTITION_ADDRESS) != 0) { printf("%s() no valid partition table found\n", __func__); return -1; } #endif 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 defined by CONFIG_CONS_INDEX (via menuconfig) */ struct serial_device *default_serial_console(void) { enable_uart2_pin_mux(); /* Provide UART on UART2 regardless of boot mode */ return &eserial3_device; } #ifndef CONFIG_SKIP_LOWLEVEL_INIT static const struct ddr_data ddr3_data = { /* Ratios were optimized by DDR3 training software from TI */ /* TODO: Evaluate new values and update */ .datardsratio0 = 0x39, /* 0x39 */ .datawdsratio0 = 0x3f, /* 0x40 */ .datafwsratio0 = 0x98, /* 0x96 */ .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_fix_config(void) { int rc; uint8_t val; /* Workaround for pre v1.3 config (reports ID 0x3e -> 3.14) */ rc = da9063_get_reg(PMIC_REG_CONFIG_ID, &val); if (!rc && ((val == 0x3E) || (val == 0x12))) { printf("Detected pre v1.3 PMIC config. Fixing registers\n"); /* Include +3V3_SENSORS (LDO3) in sequencing and enable manually */ (void)da9063_set_reg(PMIC_REG_ID_4_3, 0x07 /* Slot 7 */); (void)da9063_set_reg(PMIC_REG_LDO3_CONT, PMIC_LDOx_EN_MASK); /* * Set AUTO Bit for sequencer controlled supplies, so that they get * enabled when leaving power down state */ (void)da9063_set_reg(PMIC_REG_CONFIG_E, 0x3B); (void)da9063_set_reg(PMIC_REG_CONFIG_G, 0x44); /* * Clear sequencer target state bit (xxx_CONT::xxx_CONF bit), so that * regulators are disabled when entering power down state. * Keep EN bit enabled in order not to disable supply. */ (void)da9063_set_reg(PMIC_REG_LDO3_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_LDO7_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_BCORE1_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_BCORE2_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_BPERI_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_BIO_CONT, PMIC_LDOx_EN_MASK); (void)da9063_set_reg(PMIC_REG_BMEM_CONT, PMIC_LDOx_EN_MASK); } } static void pmic_disable_auto_mode(void) { /* * In some rare cases the automatic mode switching between synchronous and sleep * mode can lead to PMIC issues. In this case the rail output is disabled. * The following code fixes the operation mode to synchronous operation. */ uint8_t val = 0; /* Check whether config has automatic mode defined */ (void)da9063_get_reg(PMIC_REG_BCORE1_CONF, &val); if ((val & PMIC_CONF_MODE_MASK) == PMIC_CONF_MODE_AUTO) { (void)da9063_set_reg(PMIC_REG_BCORE1_CONF, PMIC_CONF_MODE_SYNC | 0x01); (void)da9063_set_reg(PMIC_REG_BCORE2_CONF, PMIC_CONF_MODE_SYNC | 0x01); (void)da9063_set_reg(PMIC_REG_BPERI_CONF, PMIC_CONF_MODE_SYNC | 0x01); (void)da9063_set_reg(PMIC_REG_BIO_CONF, PMIC_CONF_MODE_SYNC | 0x01); (void)da9063_set_reg(PMIC_REG_BMEM_CONF, PMIC_CONF_MODE_SYNC | 0x01); } } 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(); /* Fix old configs (mainly prototype boards) */ if (hw_type == 21) { pmic_fix_config(); /* Disable automatic mode switching */ pmic_disable_auto_mode(); } /* Enable +3V3_GNSS (LDO6) */ (void)da9063_set_reg(PMIC_REG_LDO6_CONT, PMIC_LDOx_EN_MASK); mdelay(2); /* Enable +5V_CAN (LDO11 Switch) */ (void)da9063_set_reg(PMIC_REG_LDO11_CONT, PMIC_LDOx_EN_MASK); mdelay(2); pmic_ignition_gate_on(); if (hw_type == 21) { /* hw21: trim RTC to compensate +18ppm crystal deviation */ (void)da9063_set_reg(PMIC_REG_TRIM_CLDR, (-18*10)/19); } else { /* hw26 does not need trimming */ (void)da9063_set_reg(PMIC_REG_TRIM_CLDR, 0); } da9063_release_i2c_bus(bus); } struct reset_registers { uint32_t value; uint32_t value_crc; }; #ifdef CONFIG_NRSW_BUILD 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 static void init_bd_spl(void) { hw_type = 21; /* Assume hw21, unless BD tells different */ if (read_eeprom() >= 0) { bd_get_hw_type(&hw_type); } else { puts("Could not get board ID.\n"); } } 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(); /* Get board descriptor */ init_bd_spl(); /* Setup PMIC */ init_pmic_spl(); init_leds(); set_status_led(1, 0); /* Red */ set_indicator_led(1, 0); /* Red */ #ifndef CONFIG_NRSW_BUILD /* UI detection */ REQUEST_AND_SET_GPIO(GPIO_RST_UI_N); ui_init(CONFIG_UI_I2C_BUS); ui_set_status_led(1, 0); /* Red */ ui_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_uart2_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 = 5us (switch), 20us (TJA1100), 100us (SMSC8720) */ mdelay(1); gpio_set_value(GPIO_RST_ETH_N, 1); } static void init_ethernet_v2(void) { /* Put switch and PHYs in reset */ REQUEST_AND_CLEAR_GPIO(GPIO_RST_ETH_N); REQUEST_AND_CLEAR_GPIO(GPIO_RST_ETH_SW_N); /* Take switch out of reset. Minimum Reset Pulse = 5us */ mdelay(1); gpio_set_value(GPIO_RST_ETH_SW_N, 1); /* Give clocks time to stabilize */ mdelay(1); } static void init_ethernet_phys_v2(void) { /* Take Ethernet PHYs out of reset */ /* Minimum Reset Pulse = 20us (TJA1102), 100us (SMSC8720) */ mdelay(1); gpio_set_value(GPIO_RST_ETH_N, 1); } static void configure_ethernet_switch(void) { static struct spi_slave *spi = 0; if (spi == 0) { configure_module_pin_mux(spi1_pin_mux); spi_init(); /* Maximum bitrate supported by switch is 17.8 MHz */ spi = spi_setup_slave(CONFIG_SJA1105_SPI_BUS, CONFIG_SJA1105_SPI_CS, 15000000 /* Frequency 15 MHz */, SPI_MODE_1); sja1105_init(spi); } spi_claim_bus(spi); /* 1ms delay after init before first SPI acces */ mdelay(1); sja1105_configure_firmware(); /* Configure clocks */ sja1105_configure_mode_and_clocks(); /* Configure IO pads */ sja1105_configure_io(); spi_release_bus(spi); } static void configure_broadr_phys(void) { unsigned char phy_start; unsigned char phy; const char *devname; int err = 0; if (hw_ver == 1) { phy_start = 6; /* HW v1.0 has PHYs on 6 and 7, by accident */ } else { phy_start = 2; /* From HW v2.0 addresses are correct at 2 and 3 */ } /* Get current device */ devname = miiphy_get_current_dev(); /* Configure TJA1100 BroadR PHY ports as slave and restart FSM */ for (phy = phy_start; phy <= phy_start+1 && err == 0; phy++) { /* Extended control register : bit 15 -> Link control disabled */ if (0 != miiphy_write (devname, phy, 0x11, 0x0004)) { err++; } /* Configuration register 1 : bit 15 -> PHY configured as Slave */ if (0 != miiphy_write (devname, phy, 0x12, 0x0910)) { err++; } /* Extended control register : link control enable and training restart */ if (0 != miiphy_write (devname, phy, 0x11, 0x9A04)) { err++; } } if (err != 0) { puts("BroadR not ready, "); } else { puts("BroadR ready, "); } } 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); } #ifndef CONFIG_NRSW_BUILD static void init_user_module(void) { int bus; bus = da9063_claim_i2c_bus(); puts("UM: "); REQUEST_AND_CLEAR_GPIO(GPIO_RST_UM_N); /* Assert reset (active low) */ REQUEST_AND_CLEAR_GPIO(GPIO_CTRL_WDIS_N) /* TODO: CHECK */ /* TODO: Should this be done at first power up as well? */ da9063_set_gpio(PMIC_UM_SUPPLY_EN_IO, 0); /* Switch Supply off */ mdelay(30); /* Give time to discharge output */ da9063_set_gpio(PMIC_UM_SUPPLY_VSEL_IO, 0); /* Set voltage to 3.3V */ mdelay(1); da9063_set_gpio(PMIC_UM_SUPPLY_EN_IO, 1); /* Enable Supply */ mdelay(10); gpio_direction_input(GPIO_RST_UM_N); /* Release reset (open drain) */ mdelay(10); da9063_release_i2c_bus(bus); mdelay(200); /* Give module some time to boot */ um_init(CONFIG_UM_I2C_BUS); /* Try to detect user module */ if (um_present()) { const char* type = um_type_as_str(); uint8_t ver = 0; uint8_t rev = 0; um_version(&ver, &rev); printf("%s V%d.%d\n", type, ver, rev); } else { puts("N/A\n"); } } #endif static void init_sim_mux(void) { /* * Switch pluggable micro SIM to onboard modem (mux = 1) */ REQUEST_AND_SET_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); } static void init_timepulse(void) { /* * Configure timepulse as input. * * Note: * Was output on HW21, function SIM_PRES_N, never worked. * Therefore reused as timepulse input on hw26. */ /* * Action: None, just leave pin at reset default = input */ } #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); #ifndef CONFIG_NRSW_BUILD /* UI detection */ REQUEST_AND_SET_GPIO(GPIO_RST_UI_N); ui_init(CONFIG_UI_I2C_BUS); #endif /* Let user know we're starting */ init_leds(); set_status_led(1, 1); /* Orange */ set_indicator_led(0, 0); /* Off */ #ifndef CONFIG_NRSW_BUILD ui_set_status_led(1, 1); /* Orange */ ui_set_indicator_led(0, 0); /* Off */ #endif printf("OSC: %lu MHz\n", get_osclk()/1000000); return 0; } #if !defined(CONFIG_SPL_BUILD) void set_console(void) { const char *defaultconsole = getenv("defaultconsole"); if (defaultconsole == 0) { /* Use the default console */ setenv("defaultconsole", "ttyS2"); } } #ifdef CONFIG_NRSW_BUILD static void set_devicetree_name(void) { char devicetreename[64]; /* add hardware versions to environment */ if (bd_get_devicetree(devicetreename, sizeof(devicetreename)) != 0) { printf("Devicetree name not found, using default name\n"); strcpy(devicetreename, "am335x-nmhw21-prod1.dtb"); } setenv("fdt_image", devicetreename); } static void set_root_partition(void) { int boot_partition; /* add active root partition to environment */ boot_partition = bd_get_boot_partition(); if (boot_partition > 1) { boot_partition = 0; } /* mmcblk1p1 => root0, mmcblk1p2 => root1 so +1 */ setenv_ulong("root_part", boot_partition + 1); } #endif static void get_variant_name(void) { bd_get_variantname(hw_variant_name, sizeof(hw_variant_name)); printf("SYS: %s\n", hw_variant_name); } #ifdef CONFIG_NRSW_BUILD static void get_hw_version(void) { char hw_versions[16]; char new_env[256]; /* current bootargs = 84 bytes */ bd_get_hw_version(&hw_ver, &hw_rev); bd_get_hw_patch(&hw_patch); bd_get_hw_type(&hw_type); if (hw_type == 0) { /* Fallback to HW21 if tag is not present */ hw_type = 21; } printf("HW%02d: V%d.%d\n", hw_type, hw_ver, hw_rev); /* 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); } #else static void get_hw_version(void) { bd_get_hw_version(&hw_ver, &hw_rev); bd_get_hw_patch(&hw_patch); bd_get_hw_type(&hw_type); if (hw_type == 0) { /* Fallback to HW21 if tag is not present */ hw_type = 21; } printf("MB: V%d.%d (HW%2d)\n", hw_ver, hw_rev, hw_type); } #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); /* Quirk: * If reported version is 3.14, this is one of the 5 prototypes which have * been programmed multiple times --> Treat these as 1.0 */ if ((ver == 3) && (rev == 14)) { ver = 1; rev = 0; } 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); } 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 */ #ifdef CONFIG_NRSW_BUILD /* Assumes status LED is orange */ set_status_led(0, 0); ui_set_top_led(0, 0); #endif while (pulses) { udelay(pulse_width); set_status_led(1, 1); #ifndef CONFIG_NRSW_BUILD ui_set_status_led(1, 1); #endif udelay(pulse_width); set_status_led(0, 0); #ifndef CONFIG_NRSW_BUILD ui_set_status_led(0, 0); #endif pulses--; } udelay(pulse_width); set_status_led(1, 1); /* Orange */ #ifndef CONFIG_NRSW_BUILD ui_set_status_led(1, 1); #endif } #define RESET_CHECK_PERIOD (100) /* 100 ms */ typedef enum _action_t { NONE = 0, WAIT, FACTORY_RESET, RECOVERY_BOOT } action_t; static void uart2_rx_gpio_pin_mux(void) { /* Configure UART2 rxd pin as GPIO */ static struct module_pin_mux uart2_gpio_pin_mux[] = { {OFFSET(spi0_sclk), (MODE(7) | PULLUDEN | PULLUP_EN | RXACTIVE)}, /* (A17) uart2_rxd = gpio0_2 */ {-1} }; configure_module_pin_mux(uart2_gpio_pin_mux); } static void uart2_rx_rx_pin_mux(void) { /* Configure UART2 rxd pin as rxd function */ static struct module_pin_mux uart2_pin_mux[] = { {OFFSET(spi0_sclk), (MODE(1) | PULLUDEN | PULLUP_EN | RXACTIVE)}, /* (A17) uart2_rxd */ {-1} }; configure_module_pin_mux(uart2_pin_mux); } static bool get_rxd_state(void) { uart2_rx_gpio_pin_mux(); (void)gpio_request(GPIO_UART2_RX, ""); int val = gpio_get_value(GPIO_UART2_RX); gpio_free(GPIO_UART2_RX); uart2_rx_rx_pin_mux(); return (val == 0); } static int check_button(int time, bool button) { /* * Hardware button logic * * input: * button: current button press state (true = pressed) * logic: * - if button is not pressed initially, return action NONE for all * invocations (state NO_ACTION) * - if button pressed initially, return action WAIT and measure duration * of press (state PRESSED). * - while button is pressed, measure time, indicate actions with LED, * - when button is released, check time and decide on factory reset * or recovery boot. */ typedef enum _state_t { INIT = 0, PRESSED, DONE } state_t; static state_t state = INIT; /* Current state */ static action_t act = NONE; /* Action to report to user, returned at function end */ static int duration = 0; if (time == 0) { state = INIT; } /* TODO: Remove printf, puts */ /* printf("button: state %d button %d -> ", state, button); */ switch (state) { case INIT: if (button) { /* Pressed, start to measure time until released */ duration = 0; act = WAIT; state = PRESSED; } else { /* If not pressed, no further action is possible */ act = NONE; state = DONE; } break; case PRESSED: if (button) { duration += RESET_CHECK_PERIOD; if (duration == DURATION_FACTORY_RESET) { /* Indicate factory reset threshold */ blink_led(1); } else if (duration == DURATION_RECOVERY_BOOT) { /* Indicate recovery boot threshold and leave state */ blink_led(2); act = RECOVERY_BOOT; state = DONE; } } else { /* Check how long button was pressed */ if (duration >= DURATION_RECOVERY_BOOT) { act = RECOVERY_BOOT; state = DONE; } else if (duration >= DURATION_FACTORY_RESET) { act = FACTORY_RESET; state = DONE; } else { /* too short, nothing to do */ act = NONE; state = DONE; } } break; case DONE: /* Final state when an action has been chosen */ break; default: break; } /* printf("act %d\n", act); */ return act; } #if 0 static int check_break_length(int time, bool rx_line) { /* * RS232 RxD Reset input logic * * Treats RxD line as reset input. Line is considered active when * break condition applies (RS232 line > 3.0 V) * * input: * rx_line: true if rxd input is '0' (break condition) * logic: * see explanation in check_button() * additional logic is added to debounce input */ #define BREAK_DEB_TIME 500 typedef enum _state_t { WAIT_BREAK = 0, HAVE_BREAK, DONE } state_t; static state_t state = WAIT_BREAK; /* Current state */ static action_t act = NONE; /* Action to report to user, returned at function end */ static int duration = 0; static int wait_time = 0; if (time == 0) { state = WAIT_BREAK; wait_time = BREAK_DEB_TIME; } /* TODO: Remove */ /* printf("break length: state %d have_break_cond %d duration %d -> ", state, have_break_cond, duration); */ switch (state) { case WAIT_BREAK: if (rx_line) { /* Break detected, start to measure time */ duration = 0; wait_time = BREAK_DEB_TIME; act = WAIT; state = HAVE_BREAK; } if (wait_time > 0) { wait_time -= RESET_CHECK_PERIOD; act = WAIT; } else { /* If no break seen, stop here */ act = NONE; state = DONE; } break; case HAVE_BREAK: if (rx_line) { wait_time = BREAK_DEB_TIME; duration += RESET_CHECK_PERIOD; if (duration == DURATION_FACTORY_RESET) { /* Indicate factory reset threshold */ blink_led(1); } else if (duration == DURATION_RECOVERY_BOOT) { /* Indicate recovery boot threshold and leave state */ blink_led(2); act = RECOVERY_BOOT; /* TODO: maybe remain in state until break removed */ state = DONE; } } if (wait_time > 0) { wait_time -= RESET_CHECK_PERIOD; } else { /* puts("break condition released\n"); */ /* Check how long button was pressed */ if (duration >= DURATION_RECOVERY_BOOT) { act = RECOVERY_BOOT; state = DONE; } else if (duration >= DURATION_FACTORY_RESET) { act = FACTORY_RESET; state = DONE; } else { /* too short, nothing to do */ act = NONE; state = DONE; } } break; case DONE: /* Final state when an action has been chosen */ break; default: break; } /* printf("act %d\n", act); */ return act; } #endif static int check_break_command(int time, int has_input, int c) { /* * Break command logic * * Tries to receive the break button (system magic key) and a command to execute. * * input: * has_input: true if a character is available * c: character code (can be 0x00 for break condition, system magic key) * logic: * - waits up to 500 ms to receive a break character. if character is not seen, * stops here, action = NONE. if character is detected goes to next state * - waits up to 500 ms to receive command character. if character is not seen, * stops here, action = NONE. if character is detected define action accordingly * 'f': factory reset * 'r': recovery boot */ #define BREAK_WAIT_TIME 1000 /* Initial wait time for break character */ #define CHARACTER_WAIT_TIME 1500 /* Subsequent wait time for command */ typedef enum _state_t { WAIT_BREAK = 1, WAIT_COMMAND, DONE } state_t; static state_t state = WAIT_BREAK; /* Current state */ static action_t act = NONE; /* Action to report to user, returned at function end */ static int wait_time; /* TODO: remove */ /* printf("break command: state %d, input %d %02x -> ", state, has_input, c); */ if (time == 0) { state = WAIT_BREAK; wait_time = BREAK_WAIT_TIME; } switch (state) { case WAIT_BREAK: /* Wait for break indication */ act = WAIT; if (has_input) { if (c == 0x00) { /* puts("break detected\n"); */ wait_time = CHARACTER_WAIT_TIME; state = WAIT_COMMAND; } } if (wait_time > 0) { wait_time -= RESET_CHECK_PERIOD; } else { /* No break received, nothing to do from our side */ act = NONE; state = DONE; } break; case WAIT_COMMAND: if (has_input) { wait_time = CHARACTER_WAIT_TIME; if (c == 0x00) { /* puts("break detected\n"); */ act = WAIT; } else if (c == 'f') { blink_led(1); act = FACTORY_RESET; state = DONE; } else if (c == 'r') { blink_led(2); act = RECOVERY_BOOT; state = DONE; } else { /* Unknown command */ act = NONE; state = DONE; } } if (wait_time > 0) { wait_time -= RESET_CHECK_PERIOD; } else { /* puts("character timeout\n"); */ act = NONE; state = DONE; } break; case DONE: /* Final state when an action has been chosen */ break; default: break; } /* printf("act %d\n", act); */ return act; } static void check_reset_button(void) { /* * Runs state machines of all reset sources to find out if one detects * an action. * * SMs return * - WAIT if they are still trying to get a result. * - NONE if they know there is no action required. * - FACTORY_RESET, RECOVERY_BOOT if detected. */ action_t action = NONE; int counter = 0; do { action_t a_[3]; bool button; bool rx_line; int has_input; int c = -1; /* Get state machine inputs */ button = get_button_state(); rx_line = get_rxd_state(); has_input = tstc(); if (has_input) { c = getc(); } /* run processing state machines */ a_[0] = NONE; a_[1] = NONE; a_[2] = NONE; a_[0] = check_button(counter, button); /* a_[1] = check_break_length(counter, rx_line); */ a_[2] = check_break_command(counter, has_input, c); /* if all SMs are sure there is not action, stop here */ if ((a_[0] == NONE) && (a_[1] == NONE) && (a_[2] == NONE)) { action = NONE; break; } /* if one of the SMs has an action, stop here */ for (int i=0; i<3; i++) { if ((a_[i] == FACTORY_RESET) || (a_[i] == RECOVERY_BOOT)) { action = a_[i]; break; } } mdelay(RESET_CHECK_PERIOD); counter += RESET_CHECK_PERIOD; } while ((action != FACTORY_RESET) && (action != RECOVERY_BOOT)); switch (action) { case FACTORY_RESET: { char new_bootargs[512]; char *bootargs = getenv("bootargs"); puts("Do factory reset during boot...\n"); if (bootargs == 0) bootargs = ""; strncpy(new_bootargs, bootargs, sizeof(new_bootargs)); strncat(new_bootargs, " factory-reset", sizeof(new_bootargs)); setenv("bootargs", new_bootargs); break; } case RECOVERY_BOOT: puts("Booting recovery image...\n"); setenv("bootcmd", "run recovery"); break; default: set_indicator_led(0, 0); break; } } #endif /* !defined(CONFIG_SPL_BUILD) */ int board_late_init(void) { #if !defined(CONFIG_SPL_BUILD) #ifndef CONFIG_NRSW_BUILD int ui_ver = -1; #endif if (read_eeprom() < 0) { puts("Could not get board ID.\n"); } get_variant_name(); get_hw_version(); get_pmic_version(); #ifdef CONFIG_NRSW_BUILD set_root_partition(); set_devicetree_name(); #endif /* Initialize pins */ REQUEST_AND_CLEAR_GPIO(GPIO_WLAN_EN); REQUEST_AND_CLEAR_GPIO(GPIO_BT_EN); REQUEST_AND_SET_GPIO(CAN0_TERM_N); /* Unused on V2.0 */ REQUEST_AND_SET_GPIO(CAN1_TERM_N); #ifndef CONFIG_NRSW_BUILD ui_ver = ui_version(); switch (ui_ver) { case 1: puts("UI: V1.0\n"); break; case 2: puts("UI: V2.0\n"); break; default: puts("UI: N/A\n"); break; } #endif if (hw_ver == 1) { /* On HW v1.0 switch and PHYs share the same reset line. * Clocks for the PHYs are only present once switch is configured * Thus the switch needs to be configured once to allow to bootstrap * the PHYs. This requires the assertion of reset again, which also * reset the switch (a 2nd time). It therefore has to be configured * a 2nd time. */ init_ethernet(); configure_ethernet_switch(); /* Reset ETH system again to pin strap PHYs */ mdelay(10); gpio_set_value(GPIO_RST_ETH_N, 0); mdelay(10); gpio_set_value(GPIO_RST_ETH_N, 1); /* Configure switch again, after 2nd reset */ configure_ethernet_switch(); } else { init_ethernet_v2(); configure_ethernet_switch(); /* * Now that Ethernet switch is running, PHY clocks are present. * Take PHYs out of reset. */ init_ethernet_phys_v2(); } init_usb_hub(); #ifndef CONFIG_NRSW_BUILD init_user_module(); #endif init_sim_mux(); init_gsm(); init_gnss(); init_timepulse(); /* * Check if a user action is requested * - Short press: factory reset * - Long press: recovery boot */ check_reset_button(); set_status_led(1, 1); /* Orange */ set_indicator_led(0, 0); /* Off */ #ifndef CONFIG_NRSW_BUILD set_status_led(1, 1); /* Orange */ set_indicator_led(0, 0); /* Off */ #endif 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 = 1 } }; 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"); } } static int read_tja1102(struct mii_dev *bus, int phy_id, int dev_addr, int phy_reg) { /* Remove TJA1102 mirror(s) at address 0 from bus */ if (phy_id == 0) { return -1; } /* * Also report ID registers 2&3 for 2nd PHY in TJA1102 * Take values from 1st PHY (ID 2) */ if (phy_id == 3 && (phy_reg == 2 || phy_reg == 3)) { phy_id = 2; } return olddev.read(bus, phy_id, dev_addr, phy_reg); } static void register_phy_hook(void) { struct mii_dev* dev; dev = miiphy_get_dev_by_name("cpsw"); if (dev != NULL) { /* Remember original structure (read pointer) */ memcpy(&olddev, dev, sizeof(olddev)); /* Override read method with SJA1002 wrapper */ dev->read = read_tja1102; } } /* 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 register_phy_hook(); /* Enable BroadR PHYs, set to slave mode */ configure_broadr_phys(); 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_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); } } #ifndef CONFIG_NRSW_BUILD static void ft_user_module(void *blob) { if (um_present()) { int node_offset; node_offset = fdt_path_offset(blob, "/user_module/"); if (node_offset != -1) { const char* model = um_type_as_str(); struct in_addr ip; struct in_addr mask; uint8_t ver = 0; uint8_t rev = 0; char hw_version[16]; char tmp[22]; um_version(&ver, &rev); um_network_address(&ip, &mask); fdt_setprop_string(blob, node_offset, "status", "okay"); fdt_setprop_string(blob, node_offset, "model", model); snprintf(hw_version, sizeof(hw_version), "%d.%d", ver, rev); fdt_setprop_string(blob, node_offset, "nm,carrierboard,version", hw_version); ip_to_string(ip, tmp); fdt_setprop_string(blob, node_offset, "ipv4-addr", tmp); ip_to_string(mask, tmp); fdt_setprop_string(blob, node_offset, "ipv4-mask", tmp); } } } static void ft_user_interface(void *blob) { int ui_hw_version = ui_version(); if (ui_hw_version == 1) { int node_offset; node_offset = fdt_path_offset(blob, "/ocp/i2c@4802a000/pca9539@74/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@4/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@5/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@6/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@7/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } } else if (ui_hw_version == 2) { int node_offset; node_offset = fdt_path_offset(blob, "/ocp/i2c@4802a000/pca9538@70/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@8/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@9/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@10/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } node_offset = fdt_path_offset(blob, "/leds/led@11/"); if (node_offset != -1) { fdt_setprop_string(blob, node_offset, "status", "okay"); } } else { /* Unknown UI version */ } } #endif static void ft_eth(void *blob) { /* * PHY ID Assignment * * broadr0 broadr1 * HW V1.0: 6 7 * HW V2.0: 3 2 * * DTB defines V2.0 settings. When running on v1.0 hardware, we change the PHY Ids as required. */ if (hw_ver == 1) { int node_offset; node_offset = fdt_path_offset(blob, "/ocp/ethernet@4a100000/mdio@4a101000/ethernet-phy@2/"); if (node_offset != -1) { fdt_setprop_u32(blob, node_offset, "", 7); fdt_setprop_u32(blob, node_offset, "reg", 7); } node_offset = fdt_path_offset(blob, "/ocp/ethernet@4a100000/mdio@4a101000/ethernet-phy@3/"); if (node_offset != -1) { fdt_setprop_u32(blob, node_offset, "", 6); fdt_setprop_u32(blob, node_offset, "reg", 6); } } } int ft_board_setup(void *blob, bd_t *bd) { ft_bootloader_version(blob); ft_hw_info(blob); #ifndef CONFIG_NRSW_BUILD ft_user_interface(blob); ft_user_module(blob); #endif ft_eth(blob); return 0; } #endif /* defined(CONFIG_OF_BOARD_SETUP) && !defined(CONFIG_SPL_BUILD) */