| /* |
| * (C) Copyright 2010,2011 |
| * NVIDIA Corporation <www.nvidia.com> |
| * |
| * 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 <common.h> |
| #include <ns16550.h> |
| #include <asm/clocks.h> |
| #include <asm/io.h> |
| |
| /* TBD: bring these over when Tegra3 is ready, then remove these #ifdefs */ |
| #include <asm/arch-tegra/bitfield.h> |
| #include <asm/arch-tegra/clk_rst.h> |
| #include <asm/arch-tegra/pmc.h> |
| #include <asm/arch-tegra/uart.h> |
| #include <asm/arch-tegra/warmboot.h> |
| #include <asm/arch/clock.h> |
| #ifdef CONFIG_TEGRA2 |
| #include <asm/arch/emc.h> |
| #include <asm/arch/gpio.h> |
| #endif |
| #include <asm/arch/pinmux.h> |
| #include <asm/arch/sys_proto.h> |
| #ifdef CONFIG_TEGRA2 |
| #include <asm/arch/usb.h> |
| #endif |
| #include <asm/arch/tegra.h> |
| |
| #ifdef CONFIG_TEGRA_SPI |
| #include <spi.h> |
| #endif |
| #ifdef CONFIG_TEGRA_I2C |
| #include <i2c.h> |
| #endif |
| #include "board.h" |
| #include "pmu.h" |
| |
| #ifdef CONFIG_TEGRA_MMC |
| #include <asm/arch/pmu.h> |
| #include <mmc.h> |
| #endif |
| #ifdef CONFIG_OF_CONTROL |
| #include <fdt_decode.h> |
| #include <libfdt.h> |
| #endif |
| |
| #ifdef CONFIG_CHROMEOS |
| #include <chromeos/common.h> |
| #endif |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| #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 |
| |
| enum { |
| /* UARTs which we can enable */ |
| UARTA = 1 << 0, |
| UARTB = 1 << 1, |
| UARTD = 1 << 3, |
| UART_ALL = 0xf |
| }; |
| |
| #ifndef CONFIG_OF_CONTROL |
| const struct tegra_sysinfo sysinfo = { |
| CONFIG_TEGRA_BOARD_STRING |
| }; |
| #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) { |
| pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA); |
| pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA); |
| pinmux_tristate_disable(PINGRP_IRRX); |
| pinmux_tristate_disable(PINGRP_IRTX); |
| } |
| 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 */ |
| } |
| |
| /* |
| * Routine: pin_mux_switches |
| * Description: Disable internal pullups for the write protect, SDIO3 write |
| * protect and the Google Recovery switch. All of these switches have external |
| * pull ups or pull downs. |
| */ |
| static void pin_mux_switches(void) |
| { |
| #if defined(CONFIG_TEGRA2) |
| /* |
| * TODO(robotboy): Move this to the FDT once there is pin mux support |
| * there. Currently all Tegra based boards use the same GPIOs for |
| * these switches. |
| */ |
| pinmux_set_pullupdown(PINGRP_ATD, PMUX_PULL_NORMAL); |
| #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, x8 on 2nd set of pins */ |
| pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4); |
| pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4); |
| pinmux_set_func(PINGRP_GME, PMUX_FUNC_SDIO4); |
| |
| pinmux_tristate_disable(PINGRP_ATB); |
| pinmux_tristate_disable(PINGRP_GMA); |
| pinmux_tristate_disable(PINGRP_GME); |
| |
| /* SDMMC3: SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */ |
| pinmux_set_func(PINGRP_SDB, PMUX_FUNC_SDIO3); |
| pinmux_set_func(PINGRP_SDC, PMUX_FUNC_SDIO3); |
| pinmux_set_func(PINGRP_SDD, PMUX_FUNC_SDIO3); |
| |
| pinmux_tristate_disable(PINGRP_SDC); |
| pinmux_tristate_disable(PINGRP_SDD); |
| pinmux_tristate_disable(PINGRP_SDB); |
| #endif |
| } |
| #endif |
| |
| #ifdef CONFIG_TEGRA3 |
| #include "../cardhu/pinmux-config-common.h" |
| #endif |
| |
| /* |
| * Routine: pinmux_init |
| * Description: Do individual peripheral pinmux configs |
| */ |
| static void pinmux_init(int uart_ids) |
| { |
| #if defined(CONFIG_TEGRA2) |
| pin_mux_uart(uart_ids); |
| #endif |
| pin_mux_switches(); |
| |
| #if defined(CONFIG_TEGRA3) |
| pinmux_config_table(tegra3_pinmux_common, |
| ARRAY_SIZE(tegra3_pinmux_common)); |
| |
| pinmux_config_table(unused_pins_lowpower, |
| ARRAY_SIZE(unused_pins_lowpower)); |
| #endif |
| } |
| |
| /** |
| * Do individual peripheral GPIO configs |
| * |
| * @param blob FDT blob with configuration information |
| */ |
| static void gpio_init(const void *blob) |
| { |
| #ifdef CONFIG_SPI_UART_SWITCH |
| gpio_early_init_uart(blob); |
| #endif |
| } |
| |
| /* |
| * Do I2C/PMU writes to bring up SD card bus power |
| * |
| */ |
| static void board_sdmmc_voltage_init(void) |
| { |
| #if defined(CONFIG_TEGRA3) && defined(CONFIG_TEGRA_MMC) |
| /* |
| * Voltage for SDMMC on Tegra30 Cardhu variants is on |
| * LDO5 and should be at 3.3. |
| * |
| * TODO(dianders): Should be in device tree. |
| */ |
| uchar ldo5_to_3_3v = PMU_LDO5_SEL(33) | PMU_LDO5_ON; |
| |
| pmu_write(PMU_LDO5_REG, &ldo5_to_3_3v, 1); |
| #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 |
| } |
| |
| /* |
| * Routine: board_init |
| * Description: Early hardware init. |
| */ |
| int board_init(void) |
| { |
| #ifdef CONFIG_VIDEO_TEGRA2 |
| lcd_pinmux_early_init(gd->blob); |
| #endif |
| /* Do clocks and UART first so that printf() works */ |
| clock_init(); |
| #ifdef CONFIG_SPI_UART_SWITCH |
| gpio_config_uart(gd->blob); |
| #endif |
| #ifdef CONFIG_USB_EHCI_TEGRA |
| board_usb_init(gd->blob); |
| #endif |
| clock_verify(); |
| #ifdef CONFIG_TEGRA_SPI |
| spi_init(); |
| #endif |
| power_det_init(); |
| |
| #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 */ |
| #ifdef CONFIG_OF_CONTROL |
| 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"); |
| #else |
| gd->bd->bi_arch_number = CONFIG_MACH_TYPE; |
| #endif |
| |
| #ifdef CONFIG_TEGRA_CLOCK_SCALING |
| board_emc_init(); |
| #endif |
| |
| #ifdef CONFIG_TEGRA2_LP0 |
| /* prepare the WB code to LP0 location */ |
| warmboot_prepare_code(TEGRA_LP0_ADDR, TEGRA_LP0_SIZE); |
| #endif |
| |
| board_sdmmc_voltage_init(); |
| |
| /* boot param addr */ |
| gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100); |
| |
| #ifdef CONFIG_CHROMEOS |
| vbexport_init(); |
| #endif |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_BOARD_EARLY_INIT_F |
| |
| int board_early_init_f(void) |
| { |
| int uart_ids = 0; /* bit mask of which UART ids to enable */ |
| ulong pllp_rate = 216000000; /* default PLLP clock rate */ |
| |
| #ifdef CONFIG_OF_CONTROL |
| struct fdt_uart uart; |
| |
| if (!fdt_decode_uart_console(gd->blob, &uart, gd->baudrate)) |
| uart_ids = 1 << uart.id; |
| #else |
| #ifdef CONFIG_TEGRA2_ENABLE_UARTA |
| uart_ids |= UARTA; |
| #endif |
| #ifdef CONFIG_TEGRA2_ENABLE_UARTB |
| uart_ids |= UARTB; |
| #endif |
| #ifdef CONFIG_TEGRA2_ENABLE_UARTD |
| uart_ids |= UARTD; |
| #endif |
| #endif /* CONFIG_OF_CONTROL */ |
| |
| /* Initialize essential common plls */ |
| #ifdef CONFIG_OF_CONTROL |
| pllp_rate = fdt_decode_clock_rate(gd->blob, "pllp", pllp_rate); |
| #endif |
| clock_early_init(pllp_rate); |
| |
| /* Initialize UART clocks */ |
| clock_init_uart(uart_ids); |
| |
| /* Initialize periph pinmuxes */ |
| pinmux_init(uart_ids); |
| |
| /* Initialize periph GPIOs */ |
| gpio_init(gd->blob); |
| |
| #ifdef CONFIG_VIDEO_TEGRA2 |
| /* Get LCD panel size */ |
| lcd_early_init(gd->blob); |
| #endif |
| |
| return 0; |
| } |
| #endif /* EARLY_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 |
| |
| /* |
| * 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 |
| }; |
| |
| /** |
| * 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(uart_ids); |
| #endif |
| |
| /* |
| * Seaboard has a UART switch on PI3. We might be a Seaboard, |
| * so flip it! |
| */ |
| #ifdef CONFIG_SPI_UART_SWITCH |
| gpio_direction_output(GPIO_PI3, 0); |
| #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); |
| } |