/* * (C) Copyright 2012 * Toradex, Inc. * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include /* do not change order of include file */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_TEGRA2 #include #include #endif #include #ifdef CONFIG_TEGRA3 #include #include #endif #include #ifdef CONFIG_USB_EHCI_TEGRA #include #endif #include #ifdef CONFIG_TEGRA_I2C #include #endif #include "board.h" #include "../../nvidia/common/pmu.h" #ifdef CONFIG_TEGRA_MMC #include #include #endif #include #include DECLARE_GLOBAL_DATA_PTR; #define NV_ADDRESS_MAP_FUSE_BASE 0x7000f800 // Register FUSE_BOOT_DEVICE_INFO_0 #define FUSE_BOOT_DEVICE_INFO_0 0x1bc // Register FUSE_RESERVED_SW_0 #define FUSE_RESERVED_SW_0 0x1c0 #define CLK_RST_BASE 0x60006000 // Register CLK_RST_CONTROLLER_MISC_CLK_ENB_0 #define CLK_RST_CONTROLLER_MISC_CLK_ENB_0 0x48 // Register APB_MISC_PP_STRAPPING_OPT_A_0 #define APB_MISC_PP_STRAPPING_OPT_A_0 0x8 typedef enum { NvBootFuseBootDevice_Sdmmc, NvBootFuseBootDevice_SnorFlash, NvBootFuseBootDevice_SpiFlash, NvBootFuseBootDevice_NandFlash, NvBootFuseBootDevice_NandFlash_x8 = NvBootFuseBootDevice_NandFlash, NvBootFuseBootDevice_NandFlash_x16 = NvBootFuseBootDevice_NandFlash, NvBootFuseBootDevice_MobileLbaNand, NvBootFuseBootDevice_MuxOneNand, NvBootFuseBootDevice_Sata, NvBootFuseBootDevice_BootRom_Reserved_Sdmmc3, /* !!! this enum is strictly used by BootRom code only !!! */ NvBootFuseBootDevice_Max, /* Must appear after the last legal item */ NvBootFuseBootDevice_Force32 = 0x7fffffff } NvBootFuseBootDevice; typedef enum { NvStrapDevSel_Emmc_Primary_x4 = 0, /* eMMC primary (x4) */ NvStrapDevSel_Emmc_Primary_x8, /* eMMC primary (x8) */ NvStrapDevSel_Emmc_Secondary_x4, /* eMMC secondary (x4) */ NvStrapDevSel_Nand, /* NAND (x8 or x16) */ NvStrapDevSel_Nand_42nm_x8, /* NAND_42nm (x8) */ NvStrapDevSel_MobileLbaNand, /* mobileLBA NAND */ NvStrapDevSel_MuxOneNand, /* MuxOneNAND */ NvStrapDevSel_Esd_x4, /* eSD (x4) */ NvStrapDevSel_SpiFlash, /* SPI Flash */ NvStrapDevSel_Snor_Muxed_x16, /* Sync NOR (Muxed, x16) */ NvStrapDevSel_Snor_Muxed_x32, /* Sync NOR (Muxed, x32) */ NvStrapDevSel_Snor_NonMuxed_x16, /* Sync NOR (NonMuxed, x16) */ NvStrapDevSel_FlexMuxOneNand, /* FlexMuxOneNAND */ NvStrapDevSel_Sata, /* Sata */ NvStrapDevSel_Emmc_Secondary_x8, /* eMMC secondary (x8) */ NvStrapDevSel_UseFuses, /* Use fuses instead */ /* The following definitions must be last. */ NvStrapDevSel_Num, /* Must appear after the last legal item */ NvStrapDevSel_Force32 = 0x7fffffff } NvStrapDevSel; typedef enum { Trdx_BootDevice_NandFlash, Trdx_BootDevice_eMMC, Trdx_BootDevice_extSD, Trdx_BootDevice_Unknown, Trdx_BootDevice_Max, /* Must appear after the last legal item */ Trdx_BootDevice_Force32 = 0x7fffffff } TrdxBootDevice; const char* sTrdxBootDeviceStr[] = { "Trdx_BootDevice_NandFlash", "Trdx_BootDevice_eMMC", "Trdx_BootDevice_extSD", "Trdx_BootDevice_Unknown" }; #if defined(CONFIG_TEGRA_CLOCK_SCALING) && !defined(CONFIG_TEGRA_I2C) #error "tegra: We need CONFIG_TEGRA_I2C to support CONFIG_TEGRA_CLOCK_SCALING" #endif #if defined(CONFIG_TEGRA_CLOCK_SCALING) && !defined(CONFIG_TEGRA_PMU) #error "tegra: We need CONFIG_TEGRA_PMU to support CONFIG_TEGRA_CLOCK_SCALING" #endif #define GENERATE_FUSE_DEV_INFO 0 static TrdxBootDevice board_get_current_bootdev(void) { unsigned reg; #if GENERATE_FUSE_DEV_INFO unsigned reg1 = 0; unsigned reg2; #endif unsigned strap_select; unsigned skip_strap; unsigned fuse_select; #if GENERATE_FUSE_DEV_INFO unsigned fuse_device_info; unsigned sdmmc_instance; #endif TrdxBootDevice boot_device; //get the latched strap pins, bit [26:29] reg = readl( ((unsigned *)NV_APB_MISC_BASE) + APB_MISC_PP_STRAPPING_OPT_A_0); /* printf("Strappings Reg 0x%x, BootSelect 0x%x, Recovery %x, NorBoot %x, JTAG %x, MIO_WIDTH %x, RAM_Code %x, NOR_Width %x\n", reg, (reg>>26)&0xf, (reg>>25)&0x1, (reg>>24)&0x1, (reg>>22)&0x3, (reg>>8)&0x1, (reg>>4)&0xf, (reg)&0x1); */ strap_select = (reg>>26)&0xf; #if 0 //Max: this does not work, there is more to read fuses than that //check if we can access BIT /tegra/core/include/nvbit.h et.al. clock_enable(PERIPH_ID_FUSE); //make fuses visible reg = readl( ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0); reg = reg | (1<<28); writel(reg, ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0 ); printf("1"); reg = readl( ((unsigned *)NV_ADDRESS_MAP_FUSE_BASE) + FUSE_RESERVED_SW_0); printf("2"); reg1 = readl( ((unsigned *)NV_ADDRESS_MAP_FUSE_BASE) + FUSE_BOOT_DEVICE_INFO_0); printf("3"); //make fuses invisible reg2 = readl( ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0); reg2 = reg2 & ~(1<<28); writel(reg2, ((unsigned *)CLK_RST_BASE) + CLK_RST_CONTROLLER_MISC_CLK_ENB_0 ); clock_disable(PERIPH_ID_FUSE); #else #ifdef CONFIG_TEGRA3 //simulate a T30 fuse setting reg = NvBootFuseBootDevice_Sdmmc; #else //simulate a T20 fuse setting reg = NvBootFuseBootDevice_NandFlash; #endif #endif //get the fuse 'SKIP_DEV_SEL_STRAPS', bit 3 skip_strap = (reg & 8)>>3; //get the fuse 'BOOT_DEV_SEL', bit [0:2] fuse_select = reg & 7; if(skip_strap || strap_select == NvStrapDevSel_UseFuses) { printf("Using fuses, %u\n", fuse_select); //getting fuse device info and sdmmc instance, bit 7 of fuse_device info #if GENERATE_FUSE_DEV_INFO fuse_device_info = reg1 & 0x3fff; sdmmc_instance = ((reg1 & 0x80)==0x80) ? 2 : 3; #endif switch(fuse_select) { case NvBootFuseBootDevice_Sdmmc: boot_device = Trdx_BootDevice_eMMC; break; case NvBootFuseBootDevice_NandFlash: boot_device = Trdx_BootDevice_NandFlash; break; default: boot_device = Trdx_BootDevice_Unknown; break; } } else { /* printf("Using straps, %u\n", strap_select);*/ #if GENERATE_FUSE_DEV_INFO sdmmc_instance = 3; #endif switch(strap_select) { case NvStrapDevSel_Emmc_Primary_x4: case NvStrapDevSel_Emmc_Secondary_x4: case NvStrapDevSel_Emmc_Secondary_x8: case NvStrapDevSel_Esd_x4: boot_device = Trdx_BootDevice_extSD; break; case NvStrapDevSel_Nand: case NvStrapDevSel_Nand_42nm_x8: boot_device = Trdx_BootDevice_NandFlash; break; default: boot_device = Trdx_BootDevice_Unknown; break; } } /* if(boot_device < sizeof(sTrdxBootDeviceStr)/sizeof(sTrdxBootDeviceStr[0])) { printf("%s is used\n",sTrdxBootDeviceStr[boot_device]); } */ return boot_device; } static void board_voltage_init(void); enum { /* UARTs which we can enable */ UARTA = 1 << 0, UARTB = 1 << 1, UARTD = 1 << 3, UART_ALL = 0xf }; #if defined(BOARD_LATE_INIT) && (defined(CONFIG_TRDX_CFG_BLOCK_OFFSET) || \ defined(CONFIG_REVISION_TAG) || defined(CONFIG_SERIAL_TAG)) static unsigned char *config_block = NULL; #endif #ifdef CONFIG_HW_WATCHDOG static int i2c_is_initialized = 0; #endif /* * Routine: timer_init * Description: init the timestamp and lastinc value */ int timer_init(void) { reset_timer(); return 0; } static void enable_uart(enum periph_id pid) { /* Assert UART reset and enable clock */ reset_set_enable(pid, 1); clock_enable(pid); clock_ll_set_source(pid, 0); /* UARTx_CLK_SRC = 00, PLLP_OUT0 */ /* wait for 2us */ udelay(2); /* De-assert reset to UART */ reset_set_enable(pid, 0); } /* * Routine: clock_init_uart * Description: init clock for the UART(s) */ static void clock_init_uart(int uart_ids) { if (uart_ids & UARTA) enable_uart(PERIPH_ID_UART1); if (uart_ids & UARTB) enable_uart(PERIPH_ID_UART2); if (uart_ids & UARTD) enable_uart(PERIPH_ID_UART4); } /* * Routine: pin_mux_uart * Description: setup the pin muxes/tristate values for the UART(s) */ static void pin_mux_uart(int uart_ids) { #if defined(CONFIG_TEGRA2) if (uart_ids & UARTA) { /* Disable UART1 where primary function */ pinmux_tristate_enable(PINGRP_IRRX); pinmux_tristate_enable(PINGRP_IRTX); pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_GMI); pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_GMI); pinmux_tristate_enable(PINGRP_SDB); pinmux_tristate_enable(PINGRP_SDD); pinmux_set_func(PINGRP_SDB, PMUX_FUNC_PWM); pinmux_set_func(PINGRP_SDD, PMUX_FUNC_PWM); pinmux_set_func(PINGRP_SDMMC1, PMUX_FUNC_UARTA); pinmux_tristate_disable(PINGRP_SDMMC1); } if (uart_ids & UARTB) { pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA); pinmux_tristate_disable(PINGRP_UAD); } if (uart_ids & UARTD) { pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD); pinmux_tristate_disable(PINGRP_GMC); } #endif /* CONFIG_TEGRA2 */ } #ifdef CONFIG_TEGRA_MMC /* * Routine: pin_mux_mmc * Description: setup the pin muxes/tristate values for the SDMMC(s) */ static void pin_mux_mmc(void) { #ifdef CONFIG_TEGRA2 /* SDMMC4: config 3, x4 on 2nd set of pins */ pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4); pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4); pinmux_tristate_disable(PINGRP_ATB); pinmux_tristate_disable(PINGRP_GMA); #endif } #endif #ifdef CONFIG_TEGRA3 #include "../colibri_t30/pinmux-config-common.h" #endif /* * Routine: pinmux_init * Description: Do individual peripheral pinmux configs */ #if defined(CONFIG_TEGRA3) static void pinmux_init(void) { pinmux_config_table(tegra3_pinmux_common, ARRAY_SIZE(tegra3_pinmux_common)); pinmux_config_table(unused_pins_lowpower, ARRAY_SIZE(unused_pins_lowpower)); } #endif static void init_uarts(const void *blob) { int uart_ids = 0; /* bit mask of which UART ids to enable */ struct fdt_uart uart; if (!fdt_decode_uart_console(blob, &uart, gd->baudrate)) uart_ids = 1 << uart.id; /* Initialize UART clocks */ clock_init_uart(uart_ids); /* Initialize periph pinmuxes */ #if defined(CONFIG_TEGRA2) pin_mux_uart(uart_ids); #endif } /* * Routine: power_det_init * Description: turn off power detects */ static void power_det_init(void) { #if defined(CONFIG_TEGRA2) struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; /* turn off power detects */ writel(0, &pmc->pmc_pwr_det_latch); writel(0, &pmc->pmc_pwr_det); #endif } #if defined(CONFIG_TEGRA3) static void enable_clock(enum periph_id pid, int src) { /* Assert reset and enable clock */ reset_set_enable(pid, 1); clock_enable(pid); /* Use 'src' if provided, else use default */ if (src != -1) clock_ll_set_source(pid, src); /* wait for 2us */ udelay(2); /* De-assert reset */ reset_set_enable(pid, 0); } /* Init misc clocks for kernel booting */ static void clock_init_misc(void) { /* 0 = PLLA_OUT0, -1 = CLK_M (default) */ enable_clock(PERIPH_ID_I2S0, -1); enable_clock(PERIPH_ID_I2S1, 0); enable_clock(PERIPH_ID_I2S2, 0); enable_clock(PERIPH_ID_I2S3, 0); enable_clock(PERIPH_ID_I2S4, -1); enable_clock(PERIPH_ID_SPDIF, -1); } #endif /* * Routine: board_init * Description: Early hardware init. */ int board_init(void) { #ifdef CONFIG_VIDEO_TEGRA tegra_lcd_check_next_stage(gd->blob, 0); #endif #ifdef CONFIG_DELAY_CONSOLE init_uarts(gd->blob); #endif /* Do clocks and UART first so that printf() works */ clock_init(); #ifdef CONFIG_USB_EHCI_TEGRA board_usb_init(gd->blob); #endif clock_verify(); power_det_init(); (void)board_get_current_bootdev(); #ifdef CONFIG_TEGRA_I2C /* Ramp up the core voltage, then change to full CPU speed */ i2c_init_board(); #endif #ifdef CONFIG_TEGRA_CLOCK_SCALING pmu_set_nominal(); arch_full_speed(); #endif /* board id for Linux */ gd->bd->bi_arch_number = fdt_decode_get_machine_arch_id(gd->blob); if (gd->bd->bi_arch_number == -1U) printf("Warning: No /config/machine-arch-id defined in fdt\n"); #ifdef CONFIG_TEGRA_CLOCK_SCALING board_emc_init(); #endif board_voltage_init(); #ifdef CONFIG_HW_WATCHDOG i2c_is_initialized = 1; #endif #ifdef CONFIG_TEGRA_LP0 /* prepare the WB code to LP0 location */ //ToDo: determine LP0 address dynamically warmboot_prepare_code(TEGRA_LP0_ADDR, TEGRA_LP0_SIZE); #endif /* boot param addr */ gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100); return 0; } #ifdef CONFIG_BOARD_EARLY_INIT_F int board_early_init_f(void) { ulong pllp_rate = 216000000; /* default PLLP clock rate */ /* Initialize essential common plls */ pllp_rate = fdt_decode_clock_rate(gd->blob, "pllp", pllp_rate); clock_early_init(pllp_rate); #ifdef CONFIG_TEGRA3 pinmux_init(); #endif #ifndef CONFIG_DELAY_CONSOLE init_uarts(gd->blob); #endif #if defined(CONFIG_TEGRA3) /* Initialize misc clocks for kernel booting */ clock_init_misc(); #endif #ifdef CONFIG_VIDEO_TEGRA /* Get LCD panel size */ lcd_early_init(gd->blob); #endif return 0; } #endif /* EARLY_INIT */ #ifdef BOARD_LATE_INIT int board_late_init(void) { char env_str[256 ]; #ifdef CONFIG_TRDX_CFG_BLOCK_OFFSET char *addr_str, *end; unsigned char bi_enetaddr[6] = {0, 0, 0, 0, 0, 0}; /* Ethernet address */ int i; unsigned char *mac_addr; unsigned char mac_addr00[6] = {0, 0, 0, 0, 0, 0}; size_t size = 4096; unsigned char toradex_oui[3] = { 0x00, 0x14, 0x2d }; int valid = 0; unsigned int offset; int ret; #endif /* CONFIG_TRDX_CFG_BLOCK_OFFSET */ #ifdef CONFIG_VIDEO_TEGRA /* Make sure we finish initing the LCD */ tegra_lcd_check_next_stage(gd->blob, 1); #endif #ifdef CONFIG_TRDX_CFG_BLOCK_OFFSET /* Allocate RAM area for config block */ config_block = malloc(size); if (!config_block) { printf("Not enough malloc space available!\n"); return -1; } for (i = 0; i < 2; i++) { if (i == 0) offset = CONFIG_TRDX_CFG_BLOCK_OFFSET; else offset = CONFIG_TRDX_CFG_BLOCK_OFFSET2; /* Clear it */ memset((void *)config_block, 0, size); /* Read production parameter config block */ #ifdef CONFIG_CMD_NAND ret = nand_read_skip_bad(&nand_info[0], offset, &size, (unsigned char *) config_block); #endif /* Check validity */ if ( (ret==0) && (size>0) ) { mac_addr = config_block + 8; if (!(memcmp(mac_addr, toradex_oui, 3))) { valid = 1; break; } } else { /* probably there is no flash, * give up reading the config block, the nand flash layer crashes on the second attempt */ break; } } /* Check validity */ if (!valid) { printf("Missing Colibri config block\n"); memset((void *)config_block, 0, size); } else { /* Get MAC address from environment */ if ((addr_str = getenv("ethaddr")) != NULL) { for (i = 0; i < 6; i++) { bi_enetaddr[i] = addr_str ? simple_strtoul(addr_str, &end, 16) : 0; if (addr_str) { addr_str = (*end) ? end + 1 : end; } } } /* Set Ethernet MAC address from config block if not already set */ if (memcmp(mac_addr00, bi_enetaddr, 6) == 0) { sprintf(env_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); setenv("ethaddr", env_str); #ifndef CONFIG_ENV_IS_NOWHERE saveenv(); #endif } } /* Default memory arguments */ if (!getenv("memargs")) { if (gd->ram_size == 0x10000000) { /* 256 MB */ setenv("memargs", "mem=148M@0M fbmem=12M@148M nvmem=96M@160M"); } else { /* 512 MB */ setenv("memargs", "mem=372M@0M fbmem=12M@372M nvmem=128M@384M"); } } #endif /* CONFIG_TRDX_CFG_BLOCK_OFFSET */ /* set the nand kernel offset */ if (!getenv("lnxoffset")) { sprintf(env_str, "0x%x", (unsigned)(gd->kernel_offset)); setenv("lnxoffset", env_str); } /* set the mtdparts string */ if (!getenv("mtdparts")) { sprintf(env_str, "mtdparts=tegra_nand:%uK@%uK(userspace)", (unsigned)(gd->rootfs_size), (unsigned)(gd->rootfs_offset) ); setenv("mtdparts", env_str); } return 0; } #endif /* BOARD_LATE_INIT */ #ifdef CONFIG_TEGRA_MMC /* this is a weak define that we are overriding */ int board_mmc_init(bd_t *bd) { debug("board_mmc_init called\n"); /* Enable muxes, etc. for SDMMC controllers */ pin_mux_mmc(); tegra_mmc_init(gd->blob); return 0; } #endif /* * MK: Do I2C/PMU writes to set nominal voltages for Colibri_T30 * */ static void board_voltage_init(void) { #if defined(CONFIG_TEGRA3) uchar reg, data_buffer[1]; int i; i2c_set_bus_num(0); /* PMU is on bus 0 */ //switch v-ddr ram to 1.35V data_buffer[0] = 0x3f; reg = 0x25; for (i = 0; i < MAX_I2C_RETRY; ++i) { if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1)) udelay(100); } //switch v-core to 1.2V data_buffer[0] = VDD_CORE_NOMINAL_T30; reg = PMU_CORE_VOLTAGE_START_REG; for (i = 0; i < MAX_I2C_RETRY; ++i) { if (i2c_write(PMU_CORE_I2C_ADDRESS, reg, 1, data_buffer, 1)) udelay(100); } data_buffer[0] = VDD_CORE_NOMINAL_T30; reg = PMU_CORE_VOLTAGE_DVFS_REG; for (i = 0; i < MAX_I2C_RETRY; ++i) { if (i2c_write(PMU_CORE_I2C_ADDRESS, reg, 1, data_buffer, 1)) udelay(100); } #ifndef CONFIG_HW_WATCHDOG //disable watchdog data_buffer[0] = 0x10; /* kick the dog once before disabling it or disabling will fail */ reg = 0x54; for (i = 0; i < MAX_I2C_RETRY; ++i) { if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1)) udelay(100); } data_buffer[0] = 0xf0; /* at least one of the reserved bits must be '1' or disabling will fail */ reg = 0x69; for (i = 0; i < MAX_I2C_RETRY; ++i) { if (i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1)) udelay(100); } #endif #endif } #ifdef CONFIG_REVISION_TAG u32 get_board_rev(void) { #ifdef BOARD_LATE_INIT int i; unsigned short major = 0, minor = 0, release = 0; size_t size = 4096; if(config_block == NULL) { return 0; } /* Parse revision information in config block */ for (i = 0; i < (size - 8); i++) { if (config_block[i] == 0x02 && config_block[i+1] == 0x40 && config_block[i+2] == 0x08) { break; } } major = (config_block[i+3] << 8) | config_block[i+4]; minor = (config_block[i+5] << 8) | config_block[i+6]; release = (config_block[i+7] << 8) | config_block[i+8]; /* Check validity */ if (major) return ((major & 0xff) << 8) | ((minor & 0xf) << 4) | ((release & 0xf) + 0xa); else return 0; #else return 0; #endif /* BOARD_LATE_INIT */ } #endif /* CONFIG_REVISION_TAG */ #ifdef CONFIG_SERIAL_TAG void get_board_serial(struct tag_serialnr *serialnr) { #ifdef BOARD_LATE_INIT int array[8]; int i; unsigned int serial = 0; unsigned int serial_offset = 11; if(config_block == NULL) { serialnr->low = 0; serialnr->high = 0; return; } /* Get MAC address from config block */ memcpy(&serial, config_block + serial_offset, 3); serial = ntohl(serial); serial >>= 8; /* Check validity */ if (serial) { /* Convert to Linux serial number format (hexadecimal coded decimal) */ i = 7; while (serial) { array[i--] = serial % 10; serial /= 10; } serial = 0; for (i = 0; i < 8; i++) { serial += array[i]; serial *= 16; } serial /= 16; } serialnr->low = serial; #else serialnr->low = 0; #endif /* BOARD_LATE_INIT */ serialnr->high = 0; } #endif /* CONFIG_SERIAL_TAG */ /* * Possible UART locations: we ignore UARTC at 0x70006200 and UARTE at * 0x70006400, since we don't have code to init them */ static u32 uart_reg_addr[] = { NV_PA_APB_UARTA_BASE, NV_PA_APB_UARTB_BASE, NV_PA_APB_UARTD_BASE, 0 }; #ifdef CONFIG_HW_WATCHDOG /* * kick watchdog in PMU * */ extern void hw_watchdog_reset(void) { #if defined(CONFIG_TEGRA3) uchar reg, data_buffer[1]; static unsigned long last_kick = 0; unsigned long now; // only kick the watchdog every 10 seconds, the watchdog timeout is 100s now = get_ticks(); //first kick after 10 seconds if( !i2c_is_initialized || (last_kick == 0) ) { last_kick = now; } else if( (signed)(now - last_kick) > (10 * 1000) ) { last_kick = now; data_buffer[0] = 0x10; reg = 0x54; i2c_write(PMU_I2C_ADDRESS, reg, 1, data_buffer, 1); } #endif } #endif /** * Send out serial output wherever we can. * * This function produces a low-level panic message, after setting PLLP * to the given value. * * @param pllp_rate Required PLLP rate (408000000 or 216000000) * @param str String to output */ static void send_output_with_pllp(ulong pllp_rate, const char *str) { int uart_ids = UART_ALL; /* turn it all on! */ u32 *uart_addr; int clock_freq, multiplier, baudrate, divisor; clock_early_init(pllp_rate); /* Try to enable all possible UARTs */ clock_init_uart(uart_ids); pin_mux_uart(uart_ids); #ifdef CONFIG_TEGRA3 /* Until we sort out pinmux, we must do the global Tegra3 init */ pinmux_init(); #endif /* * Now send the string out all the Tegra UARTs. We don't try all * possible configurations, but this could be added if required. */ clock_freq = pllp_rate; multiplier = CONFIG_DEFAULT_NS16550_MULT; baudrate = CONFIG_BAUDRATE; divisor = (clock_freq + (baudrate * (multiplier / 2))) / (multiplier * baudrate); for (uart_addr = uart_reg_addr; *uart_addr; uart_addr++) { const char *s; NS16550_init((NS16550_t)*uart_addr, divisor); for (s = str; *s; s++) { NS16550_putc((NS16550_t)*uart_addr, *s); if (*s == '\n') NS16550_putc((NS16550_t)*uart_addr, '\r'); } } } /* * This is called when we have no console. About the only reason that this * happen is if we don't have a valid fdt. So we don't know what kind of * Tegra board we are. We blindly try to print a message every which way we * know. */ void board_panic_no_console(const char *str) { /* We don't know what PLLP to use, so try both */ send_output_with_pllp(216000000, str); send_output_with_pllp(408000000, str); }