diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bb9b47b..0a4f821 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -136,9 +136,16 @@ select COMMON_CLK config ARCH_TEGRA - bool "Nvidia Tegra-based boards" - select CPU_ARM926T + bool "NVIDIA Tegra" + select CPU_V7 select HAS_DEBUG_LL + select BUILTIN_DTB + select COMMON_CLK + select CLKDEV_LOOKUP + select GPIOLIB + select GPIO_TEGRA + select OFDEVICE + select OFTREE config ARCH_ZYNQ bool "Xilinx Zynq-based boards" diff --git a/arch/arm/boards/toshiba-ac100/Makefile b/arch/arm/boards/toshiba-ac100/Makefile index 4d321e1..dcfc293 100644 --- a/arch/arm/boards/toshiba-ac100/Makefile +++ b/arch/arm/boards/toshiba-ac100/Makefile @@ -1,3 +1 @@ obj-y += board.o -obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial.o -lwl-y += lowlevel.o diff --git a/arch/arm/boards/toshiba-ac100/board.c b/arch/arm/boards/toshiba-ac100/board.c index 0eb85c5..51232ef 100644 --- a/arch/arm/boards/toshiba-ac100/board.c +++ b/arch/arm/boards/toshiba-ac100/board.c @@ -23,14 +23,6 @@ #include #include -static int ac100_mem_init(void) -{ - arm_add_mem_device("ram0", 0x0, SZ_512M); - - return 0; -} -mem_initcall(ac100_mem_init); - static struct ehci_platform_data ehci_pdata = { .flags = EHCI_HAS_TT, }; diff --git a/arch/arm/boards/toshiba-ac100/lowlevel.c b/arch/arm/boards/toshiba-ac100/lowlevel.c deleted file mode 100644 index 2f99d70..0000000 --- a/arch/arm/boards/toshiba-ac100/lowlevel.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include -#include - -void __naked barebox_arm_reset_vector(void) -{ - arm_cpu_lowlevel_init(); - barebox_arm_entry(0x0, SZ_512M, 0); -} diff --git a/arch/arm/boards/toshiba-ac100/serial.c b/arch/arm/boards/toshiba-ac100/serial.c deleted file mode 100644 index 880270d..0000000 --- a/arch/arm/boards/toshiba-ac100/serial.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2011 Antony Pavlov - * - * This file is part of barebox. - * 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 version 2 - * as published by the Free Software Foundation. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static struct NS16550_plat serial_plat = { - .clock = 0x75 * 115200 * 16 /* MODE_X_DIV */, - .shift = 2, -}; - -static int ac100_serial_console_init(void) -{ - /* Register the serial port */ - add_ns16550_device(DEVICE_ID_DYNAMIC, TEGRA_UARTA_BASE, 8 << serial_plat.shift, - IORESOURCE_MEM_8BIT, &serial_plat); - - return 0; -} -console_initcall(ac100_serial_console_init); diff --git a/arch/arm/configs/toshiba_ac100_defconfig b/arch/arm/configs/toshiba_ac100_defconfig index 065189d..1903910 100644 --- a/arch/arm/configs/toshiba_ac100_defconfig +++ b/arch/arm/configs/toshiba_ac100_defconfig @@ -1,4 +1,6 @@ +CONFIG_BUILTIN_DTB_NAME="tegra20-paz00" CONFIG_ARCH_TEGRA=y +CONFIG_TEGRA_UART_A=y CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y CONFIG_TEXT_BASE=0x01000000 CONFIG_BROKEN=y @@ -14,6 +16,7 @@ CONFIG_CMD_SLEEP=y # CONFIG_CMD_TRUE is not set # CONFIG_CMD_FALSE is not set +CONFIG_CMD_TFTP=y CONFIG_CMD_LOADB=y CONFIG_CMD_LOADY=y CONFIG_CMD_LOADS=y @@ -23,11 +26,10 @@ CONFIG_CMD_BOOTM_SHOW_TYPE=y CONFIG_CMD_RESET=y CONFIG_CMD_GO=y +CONFIG_CMD_OFTREE=y CONFIG_NET=y CONFIG_NET_DHCP=y CONFIG_NET_PING=y -CONFIG_CMD_TFTP=y -CONFIG_FS_TFTP=y CONFIG_NET_NETCONSOLE=y CONFIG_DRIVER_SERIAL_NS16550=y CONFIG_NET_USB=y @@ -36,4 +38,5 @@ CONFIG_USB=y CONFIG_USB_EHCI=y CONFIG_USB_STORAGE=y +CONFIG_FS_TFTP=y CONFIG_FS_FAT=y diff --git a/arch/arm/dts/tegra20-paz00.dts b/arch/arm/dts/tegra20-paz00.dts new file mode 100644 index 0000000..09ccb8b --- /dev/null +++ b/arch/arm/dts/tegra20-paz00.dts @@ -0,0 +1,12 @@ +/dts-v1/; + +/include/ "tegra20.dtsi" + +/ { + model = "Toshiba AC100 / Dynabook AZ"; + compatible = "compal,paz00", "nvidia,tegra20"; + + memory { + reg = <0x00000000 0x20000000>; + }; +}; diff --git a/arch/arm/dts/tegra20.dtsi b/arch/arm/dts/tegra20.dtsi new file mode 100644 index 0000000..b7d1e27 --- /dev/null +++ b/arch/arm/dts/tegra20.dtsi @@ -0,0 +1,41 @@ +/include/ "skeleton.dtsi" + +/ { + compatible = "nvidia,tegra20"; + + timer@60005000 { + compatible = "nvidia,tegra20-timer"; + reg = <0x60005000 0x60>; + interrupts = <0 0 0x04 + 0 1 0x04 + 0 41 0x04 + 0 42 0x04>; + }; + + tegra_car: clock { + compatible = "nvidia,tegra20-car"; + reg = <0x60006000 0x1000>; + #clock-cells = <1>; + }; + + gpio: gpio { + compatible = "nvidia,tegra20-gpio"; + reg = <0x6000d000 0x1000>; + interrupts = <0 32 0x04 + 0 33 0x04 + 0 34 0x04 + 0 35 0x04 + 0 55 0x04 + 0 87 0x04 + 0 89 0x04>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + }; + + pmc { + compatible = "nvidia,tegra20-pmc"; + reg = <0x7000e400 0x400>; + }; +}; diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index eda786b..965e7ab 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -1,11 +1,56 @@ if ARCH_TEGRA -config ARCH_TEXT_BASE - hex - default 0x31fc0000 +choice + prompt "Tegra processor type" + +config ARCH_TEGRA_2x_SOC + bool "Tegra 20" + +endchoice choice - prompt "Tegra Board Type" + prompt "Tegra debug UART" + help + This is the first serial console that gets activated by barebox. + Normally each board vendor should program a valid debug UART into + the ODMdata section of the boot configuration table, so it's a + reasonably good bet to use that. + If you know your ODMdata is broken, or you don't wish to activate + any serial console at all you can override the default here. + +config TEGRA_UART_ODMDATA + bool "ODMdata defined UART" + +config TEGRA_UART_A + bool "UART A" + +config TEGRA_UART_B + bool "UART B" + +config TEGRA_UART_C + bool "UART C" + +config TEGRA_UART_D + bool "UART D" + +config TEGRA_UART_E + bool "UART E" + +config TEGRA_UART_NONE + bool "None" + +endchoice + +# --------------------------------------------------------- + +if ARCH_TEGRA_2x_SOC + +config ARCH_TEXT_BASE + hex + default 0x00108000 + +choice + prompt "Tegra 20 Board Type" config MACH_TOSHIBA_AC100 bool "Toshiba AC100" @@ -16,4 +61,8 @@ source arch/arm/boards/toshiba-ac100/Kconfig +endif #ARCH_TEGRA_2x_SOC + +# --------------------------------------------------------- + endif diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 11915e5..f9c771f 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -1,2 +1,7 @@ -obj-y += clock.o -obj-y += reset.o +CFLAGS_tegra_avp_init.o := -mcpu=arm7tdmi -march=armv4t +lwl-y += tegra_avp_init.o +lwl-y += tegra_maincomplex_init.o +obj-y += tegra20.o +obj-y += tegra20-car.o +obj-y += tegra20-pmc.o +obj-y += tegra20-timer.o diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c deleted file mode 100644 index 82065ee..0000000 --- a/arch/arm/mach-tegra/clock.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2011 Antony Pavlov - * - * This file is part of barebox. - * 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 version 2 - * as published by the Free Software Foundation. - * - * 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. - * - */ - -/** - * @file - * @brief Clocksource based on Tegra internal timer - */ - -#include -#include -#include -#include -#include -#include -#include - -static void __iomem *timer_reg_base = (void __iomem *) (TEGRA_TMR1_BASE); - -#define timer_writel(value, reg) \ - __raw_writel(value, (u32)timer_reg_base + (reg)) -#define timer_readl(reg) \ - __raw_readl((u32)timer_reg_base + (reg)) - -static uint64_t tegra_clocksource_read(void) -{ - return timer_readl(0x10); -} - -static struct clocksource cs = { - .read = tegra_clocksource_read, - .mask = 0xffffffff, -}; - -/* FIXME: here we have no initialization. All initialization made by U-Boot */ -static int clocksource_init(void) -{ - cs.mult = clocksource_hz2mult(1000000, cs.shift); - init_clock(&cs); - - return 0; -} -core_initcall(clocksource_init); diff --git a/arch/arm/mach-tegra/include/mach/clkdev.h b/arch/arm/mach-tegra/include/mach/clkdev.h new file mode 100644 index 0000000..04b37a8 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/clkdev.h @@ -0,0 +1,7 @@ +#ifndef __ASM_MACH_CLKDEV_H +#define __ASM_MACH_CLKDEV_H + +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) + +#endif diff --git a/arch/arm/mach-tegra/include/mach/gpio.h b/arch/arm/mach-tegra/include/mach/gpio.h new file mode 100644 index 0000000..306ab4c --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/gpio.h @@ -0,0 +1 @@ +#include diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index ba478e7..b74e5d2 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -59,6 +59,9 @@ #define TEGRA_GART_BASE 0x58000000 #define TEGRA_GART_SIZE SZ_32M +#define TEGRA_UP_TAG_BASE 0x60000000 +#define TEGRA_UP_TAG_SIZE SZ_4K + #define TEGRA_RES_SEMA_BASE 0x60001000 #define TEGRA_RES_SEMA_SIZE SZ_4K diff --git a/arch/arm/mach-tegra/include/mach/lowlevel.h b/arch/arm/mach-tegra/include/mach/lowlevel.h new file mode 100644 index 0000000..b7c01c0 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/lowlevel.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/** + * @file + * @brief Boot informations provided by the Tegra SoC and it's BootROM. All + * accessor functions are a header only implementations, as they are meant to + * be used by both the main CPU complex (ARMv7) and the AVP (ARMv4). + */ + +#include +#include +#include + +/* Bootinfotable */ + +#define NV_BIT_BCTSIZE 0x38 /* size of the BCT in IRAM */ +#define NV_BIT_BCTPTR 0x3C /* location of the BCT in IRAM */ + +/* ODM data */ +#define BCT_ODMDATA_OFFSET 12 /* offset from the _end_ of the BCT */ + +#define T20_ODMDATA_RAMSIZE_SHIFT 28 +#define T20_ODMDATA_RAMSIZE_MASK (3 << T20_ODMDATA_RAMSIZE_SHIFT) +#define T20_ODMDATA_UARTTYPE_SHIFT 18 +#define T20_ODMDATA_UARTTYPE_MASK (3 << T20_ODMDATA_UARTTYPE_SHIFT) +#define T20_ODMDATA_UARTID_SHIFT 15 +#define T20_ODMDATA_UARTID_MASK (7 << T20_ODMDATA_UARTID_SHIFT) + +static inline u32 tegra_get_odmdata(void) +{ + u32 bctsize, bctptr, odmdata; + + bctsize = cpu_readl(TEGRA_IRAM_BASE + NV_BIT_BCTSIZE); + bctptr = cpu_readl(TEGRA_IRAM_BASE + NV_BIT_BCTPTR); + + odmdata = cpu_readl(bctptr + bctsize - BCT_ODMDATA_OFFSET); + + return odmdata; +} + +/* chip ID */ +#define APB_MISC_HIDREV 0x804 +#define HIDREV_CHIPID_SHIFT 8 +#define HIDREV_CHIPID_MASK (0xff << HIDREV_CHIPID_SHIFT) + +enum tegra_chiptype { + TEGRA_UNK_REV = -1, + TEGRA20 = 0, +}; + +static inline enum tegra_chiptype tegra_get_chiptype(void) +{ + u32 hidrev; + + hidrev = readl(TEGRA_APB_MISC_BASE + APB_MISC_HIDREV); + + switch ((hidrev & HIDREV_CHIPID_MASK) >> HIDREV_CHIPID_SHIFT) { + case 0x20: + return TEGRA20; + default: + return TEGRA_UNK_REV; + } +} + +static inline int tegra_get_num_cores(void) +{ + switch (tegra_get_chiptype()) { + case TEGRA20: + return 2; + break; + default: + return 0; + break; + } +} + +/* Runtime data */ +static inline int tegra_cpu_is_maincomplex(void) +{ + u32 tag0; + + tag0 = readl(TEGRA_UP_TAG_BASE); + + return (tag0 & 0xff) == 0x55; +} + +static inline uint32_t tegra20_get_ramsize(void) +{ + switch ((tegra_get_odmdata() & T20_ODMDATA_RAMSIZE_MASK) >> + T20_ODMDATA_RAMSIZE_SHIFT) { + case 1: + return SZ_256M; + default: + case 2: + return SZ_512M; + case 3: + return SZ_1G; + } +} + +static long uart_id_to_base[] = { + TEGRA_UARTA_BASE, + TEGRA_UARTB_BASE, + TEGRA_UARTC_BASE, + TEGRA_UARTD_BASE, + TEGRA_UARTE_BASE, +}; + +static inline long tegra20_get_debuguart_base(void) +{ + u32 odmdata; + int id; + + odmdata = tegra_get_odmdata(); + + /* + * Get type, we accept both "2" and "3", as they both demark a UART, + * depending on the board type. + */ + if (!(((odmdata & T20_ODMDATA_UARTTYPE_MASK) >> + T20_ODMDATA_UARTTYPE_SHIFT) & 0x2)) + return 0; + + id = (odmdata & T20_ODMDATA_UARTID_MASK) >> T20_ODMDATA_UARTID_SHIFT; + if (id > ARRAY_SIZE(uart_id_to_base)) + return 0; + + return uart_id_to_base[id]; +} + +/* reset vector for the main CPU complex */ +void tegra_maincomplex_entry(void); diff --git a/arch/arm/mach-tegra/include/mach/tegra20-car.h b/arch/arm/mach-tegra/include/mach/tegra20-car.h new file mode 100644 index 0000000..5669732 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/tegra20-car.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/* Register definitions */ +#define CRC_CLK_OUT_ENB_L 0x010 +#define CRC_CLK_OUT_ENB_L_CACHE2 (1 << 31) +#define CRC_CLK_OUT_ENB_L_VCP (1 << 29) +#define CRC_CLK_OUT_ENB_L_HOST1X (1 << 28) +#define CRC_CLK_OUT_ENB_L_DISP1 (1 << 27) +#define CRC_CLK_OUT_ENB_L_DISP2 (1 << 26) +#define CRC_CLK_OUT_ENB_L_IDE (1 << 25) +#define CRC_CLK_OUT_ENB_L_3D (1 << 24) +#define CRC_CLK_OUT_ENB_L_ISP (1 << 23) +#define CRC_CLK_OUT_ENB_L_USBD (1 << 22) +#define CRC_CLK_OUT_ENB_L_2D (1 << 21) +#define CRC_CLK_OUT_ENB_L_VI (1 << 20) +#define CRC_CLK_OUT_ENB_L_EPP (1 << 19) +#define CRC_CLK_OUT_ENB_L_I2S2 (1 << 18) +#define CRC_CLK_OUT_ENB_L_PWM (1 << 17) +#define CRC_CLK_OUT_ENB_L_TWC (1 << 16) +#define CRC_CLK_OUT_ENB_L_SDMMC4 (1 << 15) +#define CRC_CLK_OUT_ENB_L_SDMMC1 (1 << 14) +#define CRC_CLK_OUT_ENB_L_NDFLASH (1 << 13) +#define CRC_CLK_OUT_ENB_L_I2C1 (1 << 12) +#define CRC_CLK_OUT_ENB_L_I2S1 (1 << 11) +#define CRC_CLK_OUT_ENB_L_SPDIF (1 << 10) +#define CRC_CLK_OUT_ENB_L_SDMMC2 (1 << 9) +#define CRC_CLK_OUT_ENB_L_GPIO (1 << 8) +#define CRC_CLK_OUT_ENB_L_UART2 (1 << 7) +#define CRC_CLK_OUT_ENB_L_UART1 (1 << 6) +#define CRC_CLK_OUT_ENB_L_TMR (1 << 5) +#define CRC_CLK_OUT_ENB_L_RTC (1 << 4) +#define CRC_CLK_OUT_ENB_L_AC97 (1 << 3) +#define CRC_CLK_OUT_ENB_L_CPU (1 << 0) + +#define CRC_SCLK_BURST_POLICY 0x028 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_SHIFT 28 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_FIQ 8 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_IRQ 4 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_RUN 2 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_IDLE 1 +#define CRC_SCLK_BURST_POLICY_SYS_STATE_STDBY 0 + +#define CRC_SUPER_SCLK_DIV 0x02c +#define CRC_SUPER_SDIV_ENB (1 << 31) +#define CRC_SUPER_SDIV_DIS_FROM_COP_FIQ (1 << 27) +#define CRC_SUPER_SDIV_DIS_FROM_CPU_FIQ (1 << 26) +#define CRC_SUPER_SDIV_DIS_FROM_COP_IRQ (1 << 25) +#define CRC_SUPER_SDIV_DIS_FROM_CPU_IRQ (1 << 24) +#define CRC_SUPER_SDIV_DIVIDEND_SHIFT 8 +#define CRC_SUPER_SDIV_DIVIDEND_MASK (0xff << CRC_SUPER_SDIV_DIVIDEND_SHIFT) +#define CRC_SUPER_SDIV_DIVISOR_SHIFT 0 +#define CRC_SUPER_SDIV_DIVISOR_MASK (0xff << CRC_SUPER_SDIV_DIVISOR_SHIFT) + +#define CRC_CLK_CPU_CMPLX 0x04c +#define CRC_CLK_CPU_CMPLX_CPU3_CLK_STP (1 << 11) +#define CRC_CLK_CPU_CMPLX_CPU2_CLK_STP (1 << 10) +#define CRC_CLK_CPU_CMPLX_CPU1_CLK_STP (1 << 9) +#define CRC_CLK_CPU_CMPLX_CPU0_CLK_STP (1 << 8) +#define CRC_CLK_CPU_CMPLX_CPU_BRIDGE_DIV_SHIFT 0 +#define CRC_CLK_CPU_CMPLX_CPU_BRIDGE_DIV_4 3 +#define CRC_CLK_CPU_CMPLX_CPU_BRIDGE_DIV_3 2 +#define CRC_CLK_CPU_CMPLX_CPU_BRIDGE_DIV_2 1 +#define CRC_CLK_CPU_CMPLX_CPU_BRIDGE_DIV_1 0 + +#define CRC_OSC_CTRL 0x050 +#define CRC_OSC_CTRL_OSC_FREQ_SHIFT 30 +#define CRC_OSC_CTRL_OSC_FREQ_MASK (0x3 << CRC_OSC_CTRL_OSC_FREQ_SHIFT) +#define CRC_OSC_CTRL_PLL_REF_DIV_SHIFT 28 +#define CRC_OSC_CTRL_PLL_REF_DIV_MASK (0x3 << CRC_OSC_CTRL_PLL_REF_DIV_SHIFT) + +#define CRC_PLLX_BASE 0x0e0 +#define CRC_PLLX_BASE_BYPASS (1 << 31) +#define CRC_PLLX_BASE_ENABLE (1 << 30) +#define CRC_PLLX_BASE_REF_DIS (1 << 29) +#define CRC_PLLX_BASE_LOCK (1 << 27) +#define CRC_PLLX_BASE_DIVP_SHIFT 20 +#define CRC_PLLX_BASE_DIVP_MASK (0x7 << CRC_PLLX_BASE_DIVP_SHIFT) +#define CRC_PLLX_BASE_DIVN_SHIFT 8 +#define CRC_PLLX_BASE_DIVN_MASK (0x3ff << CRC_PLLX_BASE_DIVN_SHIFT) +#define CRC_PLLX_BASE_DIVM_SHIFT 0 +#define CRC_PLLX_BASE_DIVM_MASK (0xf << CRC_PLLX_BASE_DIVM_SHIFT) + +#define CRC_PLLX_MISC 0x0e4 +#define CRC_PLLX_MISC_SETUP_SHIFT 24 +#define CRC_PLLX_MISC_SETUP_MASK (0xf << CRC_PLLX_MISC_SETUP_SHIFT) +#define CRC_PLLX_MISC_PTS_SHIFT 22 +#define CRC_PLLX_MISC_PTS_MASK (0x3 << CRC_PLLX_MISC_PTS_SHIFT) +#define CRC_PLLX_MISC_DCCON (1 << 20) +#define CRC_PLLX_MISC_LOCK_ENABLE (1 << 18) +#define CRC_PLLX_MISC_LOCK_SEL_SHIFT 12 +#define CRC_PLLX_MISC_LOCK_SEL_MASK (0x3f << CRC_PLLX_MISC_LOCK_SEL_SHIFT) +#define CRC_PLLX_MISC_CPCON_SHIFT 8 +#define CRC_PLLX_MISC_CPCON_MASK (0xf << CRC_PLLX_MISC_CPCON_SHIFT) +#define CRC_PLLX_MISC_LFCON_SHIFT 4 +#define CRC_PLLX_MISC_LFCON_MASK (0xf << CRC_PLLX_MISC_LFCON_SHIFT) +#define CRC_PLLX_MISC_VCOCON_SHIFT 0 +#define CRC_PLLX_MISC_VCOCON_MASK (0xf << CRC_PLLX_MISC_VCOCON_SHIFT) + +#define CRC_RST_DEV_L_SET 0x300 +#define CRC_RST_DEV_L_SET_CACHE2 (1 << 31) +#define CRC_RST_DEV_L_SET_VCP (1 << 29) +#define CRC_RST_DEV_L_SET_HOST1X (1 << 28) +#define CRC_RST_DEV_L_SET_DISP1 (1 << 27) +#define CRC_RST_DEV_L_SET_DISP2 (1 << 26) +#define CRC_RST_DEV_L_SET_IDE (1 << 25) +#define CRC_RST_DEV_L_SET_3D (1 << 24) +#define CRC_RST_DEV_L_SET_ISP (1 << 23) +#define CRC_RST_DEV_L_SET_USBD (1 << 22) +#define CRC_RST_DEV_L_SET_2D (1 << 21) +#define CRC_RST_DEV_L_SET_VI (1 << 20) +#define CRC_RST_DEV_L_SET_EPP (1 << 19) +#define CRC_RST_DEV_L_SET_I2S2 (1 << 18) +#define CRC_RST_DEV_L_SET_PWM (1 << 17) +#define CRC_RST_DEV_L_SET_TWC (1 << 16) +#define CRC_RST_DEV_L_SET_SDMMC4 (1 << 15) +#define CRC_RST_DEV_L_SET_SDMMC1 (1 << 14) +#define CRC_RST_DEV_L_SET_NDFLASH (1 << 13) +#define CRC_RST_DEV_L_SET_I2C1 (1 << 12) +#define CRC_RST_DEV_L_SET_I2S1 (1 << 11) +#define CRC_RST_DEV_L_SET_SPDIF (1 << 10) +#define CRC_RST_DEV_L_SET_SDMMC2 (1 << 9) +#define CRC_RST_DEV_L_SET_GPIO (1 << 8) +#define CRC_RST_DEV_L_SET_UART2 (1 << 7) +#define CRC_RST_DEV_L_SET_UART1 (1 << 6) +#define CRC_RST_DEV_L_SET_TMR (1 << 5) +#define CRC_RST_DEV_L_SET_AC97 (1 << 3) +#define CRC_RST_DEV_L_SET_SYS (1 << 2) +#define CRC_RST_DEV_L_SET_COP (1 << 1) +#define CRC_RST_DEV_L_SET_CPU (1 << 0) + +#define CRC_RST_DEV_L_CLR 0x304 +#define CRC_RST_DEV_L_CLR_CACHE2 (1 << 31) +#define CRC_RST_DEV_L_CLR_VCP (1 << 29) +#define CRC_RST_DEV_L_CLR_HOST1X (1 << 28) +#define CRC_RST_DEV_L_CLR_DISP1 (1 << 27) +#define CRC_RST_DEV_L_CLR_DISP2 (1 << 26) +#define CRC_RST_DEV_L_CLR_IDE (1 << 25) +#define CRC_RST_DEV_L_CLR_3D (1 << 24) +#define CRC_RST_DEV_L_CLR_ISP (1 << 23) +#define CRC_RST_DEV_L_CLR_USBD (1 << 22) +#define CRC_RST_DEV_L_CLR_2D (1 << 21) +#define CRC_RST_DEV_L_CLR_VI (1 << 20) +#define CRC_RST_DEV_L_CLR_EPP (1 << 19) +#define CRC_RST_DEV_L_CLR_I2S2 (1 << 18) +#define CRC_RST_DEV_L_CLR_PWM (1 << 17) +#define CRC_RST_DEV_L_CLR_TWC (1 << 16) +#define CRC_RST_DEV_L_CLR_SDMMC4 (1 << 15) +#define CRC_RST_DEV_L_CLR_SDMMC1 (1 << 14) +#define CRC_RST_DEV_L_CLR_NDFLASH (1 << 13) +#define CRC_RST_DEV_L_CLR_I2C1 (1 << 12) +#define CRC_RST_DEV_L_CLR_I2S1 (1 << 11) +#define CRC_RST_DEV_L_CLR_SPDIF (1 << 10) +#define CRC_RST_DEV_L_CLR_SDMMC2 (1 << 9) +#define CRC_RST_DEV_L_CLR_GPIO (1 << 8) +#define CRC_RST_DEV_L_CLR_UART2 (1 << 7) +#define CRC_RST_DEV_L_CLR_UART1 (1 << 6) +#define CRC_RST_DEV_L_CLR_TMR (1 << 5) +#define CRC_RST_DEV_L_CLR_AC97 (1 << 3) +#define CRC_RST_DEV_L_CLR_SYS (1 << 2) +#define CRC_RST_DEV_L_CLR_COP (1 << 1) +#define CRC_RST_DEV_L_CLR_CPU (1 << 0) + +#define CRC_RST_CPU_CMPLX_SET 0x340 + +#define CRC_RST_CPU_CMPLX_CLR 0x344 diff --git a/arch/arm/mach-tegra/include/mach/tegra20-pmc.h b/arch/arm/mach-tegra/include/mach/tegra20-pmc.h new file mode 100644 index 0000000..d56b845 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/tegra20-pmc.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/* register definitions */ +#define PMC_CNTRL 0x000 +#define PMC_CNTRL_FUSE_OVERRIDE (1 << 18) +#define PMC_CNTRL_INTR_POLARITY (1 << 17) +#define PMC_CNTRL_CPUPWRREQ_OE (1 << 16) +#define PMC_CNTRL_CPUPWRREQ_POLARITY (1 << 15) +#define PMC_CNTRL_SIDE_EFFECT_LP0 (1 << 14) +#define PMC_CNTRL_AOINIT (1 << 13) +#define PMC_CNTRL_PWRGATE_DIS (1 << 12) +#define PMC_CNTRL_SYSCLK_OE (1 << 11) +#define PMC_CNTRL_SYSCLK_POLARITY (1 << 10) +#define PMC_CNTRL_PWRREQ_OE (1 << 9) +#define PMC_CNTRL_PWRREQ_POLARITY (1 << 8) +#define PMC_CNTRL_BLINK_EN (1 << 7) +#define PMC_CNTRL_GLITCHDET_DIS (1 << 6) +#define PMC_CNTRL_LATCHWAKE_EN (1 << 5) +#define PMC_CNTRL_MAIN_RST (1 << 4) +#define PMC_CNTRL_KBC_RST (1 << 3) +#define PMC_CNTRL_RTC_RST (1 << 2) +#define PMC_CNTRL_RTC_CLK_DIS (1 << 1) +#define PMC_CNTRL_KBC_CLK_DIS (1 << 0) + +#define PMC_PWRGATE_TOGGLE 0x030 +#define PMC_PWRGATE_TOGGLE_PARTID_SHIFT 0 +#define PMC_PWRGATE_TOGGLE_PARTID_MASK (0x3 << PMC_PWRGATE_TOGGLE_PARTID_SHIFT) +#define PMC_PWRGATE_TOGGLE_PARTID_CPU 0 +#define PMC_PWRGATE_TOGGLE_PARTID_TD 1 +#define PMC_PWRGATE_TOGGLE_PARTID_VE 2 +#define PMC_PWRGATE_TOGGLE_PARTID_PCX 3 +#define PMC_PWRGATE_TOGGLE_PARTID_VDE 4 +#define PMC_PWRGATE_TOGGLE_PARTID_L2C 5 +#define PMC_PWRGATE_TOGGLE_PARTID_MPE 6 +#define PMC_PWRGATE_TOGGLE_START (1 << 8) + +#define PMC_REMOVE_CLAMPING_CMD 0x034 +#define PMC_REMOVE_CLAMPING_CMD_MPE (1 << 6) +#define PMC_REMOVE_CLAMPING_CMD_L2C (1 << 5) +#define PMC_REMOVE_CLAMPING_CMD_PCX (1 << 4) +#define PMC_REMOVE_CLAMPING_CMD_VDE (1 << 3) +#define PMC_REMOVE_CLAMPING_CMD_VE (1 << 2) +#define PMC_REMOVE_CLAMPING_CMD_TD (1 << 1) +#define PMC_REMOVE_CLAMPING_CMD_CPU (1 << 0) + +#define PMC_PWRGATE_STATUS 0x038 +#define PMC_PWRGATE_STATUS_MPE (1 << 6) +#define PMC_PWRGATE_STATUS_L2C (1 << 5) +#define PMC_PWRGATE_STATUS_VDE (1 << 4) +#define PMC_PWRGATE_STATUS_PCX (1 << 3) +#define PMC_PWRGATE_STATUS_VE (1 << 2) +#define PMC_PWRGATE_STATUS_TD (1 << 1) +#define PMC_PWRGATE_STATUS_CPU (1 << 0) diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c deleted file mode 100644 index 91f9b3b..0000000 --- a/arch/arm/mach-tegra/reset.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2011 Antony Pavlov - * - * This file is part of barebox. - * 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 version 2 - * as published by the Free Software Foundation. - * - * 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. - * - */ - -/** - * @file - * @brief Resetting an malta board - */ - -#include -#include -#include - -#define PRM_RSTCTRL TEGRA_PMC_BASE - -void __noreturn reset_cpu(ulong addr) -{ - int rstctrl; - - rstctrl = __raw_readl((char *)PRM_RSTCTRL); - rstctrl |= 0x10; - __raw_writel(rstctrl, (char *)PRM_RSTCTRL); - - unreachable(); -} -EXPORT_SYMBOL(reset_cpu); diff --git a/arch/arm/mach-tegra/tegra20-car.c b/arch/arm/mach-tegra/tegra20-car.c new file mode 100644 index 0000000..3af7bdc --- /dev/null +++ b/arch/arm/mach-tegra/tegra20-car.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/** + * @file + * @brief Device driver for the Tegra 20 clock and reset (CAR) controller + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void __iomem *car_base; + +enum tegra20_clks { + cpu, ac97 = 3, rtc, timer, uarta, gpio = 8, sdmmc2, i2s1 = 11, i2c1, + ndflash, sdmmc1, sdmmc4, twc, pwm, i2s2, epp, gr2d = 21, usbd, isp, + gr3d, ide, disp2, disp1, host1x, vcp, cache2 = 31, mem, ahbdma, apbdma, + kbc = 36, stat_mon, pmc, fuse, kfuse, sbc1, nor, spi, sbc2, xio, sbc3, + dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2, + usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3, + pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb, + iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2, + uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve, + osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0, + pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1, + pll_p, pll_p_out1, pll_p_out2, pll_p_out3, pll_p_out4, pll_u, + pll_x, audio, pll_ref, twd, clk_max, +}; + +static struct clk *clks[clk_max]; + +static unsigned long get_osc_frequency(void) +{ + u32 osc_ctrl = readl(car_base + CRC_OSC_CTRL); + + switch ((osc_ctrl & CRC_OSC_CTRL_OSC_FREQ_MASK) >> + CRC_OSC_CTRL_OSC_FREQ_SHIFT) { + case 0: + return 13000000; + case 1: + return 19200000; + case 2: + return 12000000; + case 3: + return 26000000; + default: + return 0; + } +} + +static unsigned int get_pll_ref_div(void) +{ + u32 osc_ctrl = readl(car_base + CRC_OSC_CTRL); + + return 1U << ((osc_ctrl & CRC_OSC_CTRL_PLL_REF_DIV_MASK) >> + CRC_OSC_CTRL_PLL_REF_DIV_SHIFT); +} + +static int tegra20_car_probe(struct device_d *dev) +{ + car_base = dev_request_mem_region(dev, 0); + if (!car_base) + return -EBUSY; + + /* primary clocks */ + clks[clk_m] = clk_fixed("clk_m", get_osc_frequency()); + clks[clk_32k] = clk_fixed("clk_32k", 32768); + + clks[pll_ref] = clk_fixed_factor("pll_ref", "clk_m", 1, + get_pll_ref_div()); + + /* derived clocks */ + /* timer is a gate, but as it's enabled by BOOTROM we needn't worry */ + clks[timer] = clk_fixed_factor("timer", "clk_m", 1, 1); + + /* device to clock links */ + clkdev_add_physbase(clks[timer], TEGRA_TMR1_BASE, NULL); + + return 0; +} + +static __maybe_unused struct of_device_id tegra20_car_dt_ids[] = { + { + .compatible = "nvidia,tegra20-car", + }, { + /* sentinel */ + } +}; + +static struct driver_d tegra20_car_driver = { + .probe = tegra20_car_probe, + .name = "tegra20-car", + .of_compatible = DRV_OF_COMPAT(tegra20_car_dt_ids), +}; + +static int tegra20_car_init(void) +{ + return platform_driver_register(&tegra20_car_driver); +} +postcore_initcall(tegra20_car_init); diff --git a/arch/arm/mach-tegra/tegra20-pmc.c b/arch/arm/mach-tegra/tegra20-pmc.c new file mode 100644 index 0000000..b7d84d8 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20-pmc.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/** + * @file + * @brief Device driver for the Tegra 20 power management controller. + */ + +#include +#include +#include + +#include + +static void __iomem *pmc_base; + +/* main SoC reset trigger */ +void __noreturn reset_cpu(ulong addr) +{ + writel(PMC_CNTRL_MAIN_RST, pmc_base + PMC_CNTRL); + + unreachable(); +} +EXPORT_SYMBOL(reset_cpu); + +static int tegra20_pmc_probe(struct device_d *dev) +{ + pmc_base = dev_request_mem_region(dev, 0); + if (!pmc_base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + return 0; +} + +static __maybe_unused struct of_device_id tegra20_pmc_dt_ids[] = { + { + .compatible = "nvidia,tegra20-pmc", + }, { + /* sentinel */ + } +}; + +static struct driver_d tegra20_pmc_driver = { + .probe = tegra20_pmc_probe, + .name = "tegra20-pmc", + .of_compatible = DRV_OF_COMPAT(tegra20_pmc_dt_ids), +}; + +static int tegra20_pmc_init(void) +{ + return platform_driver_register(&tegra20_pmc_driver); +} +coredevice_initcall(tegra20_pmc_init); diff --git a/arch/arm/mach-tegra/tegra20-timer.c b/arch/arm/mach-tegra/tegra20-timer.c new file mode 100644 index 0000000..aafbfd4 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20-timer.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +/** + * @file + * @brief Device driver for the Tegra 20 timer, which exposes a clocksource. + */ + +#include +#include +#include +#include +#include + +/* register definitions */ +#define TIMERUS_CNTR_1US 0x10 +#define TIMERUS_USEC_CFG 0x14 + +static void __iomem *timer_base; + +static uint64_t tegra20_timer_cs_read(void) +{ + return readl(timer_base + TIMERUS_CNTR_1US); +} + +static struct clocksource cs = { + .read = tegra20_timer_cs_read, + .mask = CLOCKSOURCE_MASK(32), +}; + +static int tegra20_timer_probe(struct device_d *dev) +{ + struct clk *timer_clk; + unsigned long rate; + u32 reg; + + /* use only one timer */ + if (timer_base) + return -EBUSY; + + timer_base = dev_request_mem_region(dev, 0); + if (!timer_base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + timer_clk = clk_get(dev, NULL); + if (!timer_clk) { + dev_err(dev, "could not get clock\n"); + return -ENODEV; + } + + clk_enable(timer_clk); + + /* + * calibrate timer to run at 1MHz + * TIMERUS_USEC_CFG selects the scale down factor with bits [0:7] + * representing the divisor and bits [8:15] representing the dividend + * each in n+1 form. + */ + rate = clk_get_rate(timer_clk); + switch (rate) { + case 12000000: + reg = 0x000b; + break; + case 13000000: + reg = 0x000c; + break; + case 19200000: + reg = 0x045f; + break; + case 26000000: + reg = 0x0019; + break; + default: + reg = 0; + dev_warn(dev, "unknown timer clock rate\n"); + break; + } + writel(reg, timer_base + TIMERUS_USEC_CFG); + + cs.mult = clocksource_hz2mult(1000000, cs.shift); + init_clock(&cs); + + return 0; +} + +static __maybe_unused struct of_device_id tegra20_timer_dt_ids[] = { + { + .compatible = "nvidia,tegra20-timer", + }, { + /* sentinel */ + } +}; + +static struct driver_d tegra20_timer_driver = { + .probe = tegra20_timer_probe, + .name = "tegra20-timer", + .of_compatible = DRV_OF_COMPAT(tegra20_timer_dt_ids), +}; + +static int tegra20_timer_init(void) +{ + return platform_driver_register(&tegra20_timer_driver); +} +coredevice_initcall(tegra20_timer_init); diff --git a/arch/arm/mach-tegra/tegra20.c b/arch/arm/mach-tegra/tegra20.c new file mode 100644 index 0000000..cc2d748 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include +#include + +static struct NS16550_plat debug_uart = { + .clock = 216000000, /* pll_p rate */ + .shift = 2, +}; + +static int tegra20_add_debug_console(void) +{ + unsigned long base = 0; + + /* figure out which UART to use */ + if (IS_ENABLED(CONFIG_TEGRA_UART_NONE)) + return 0; + if (IS_ENABLED(CONFIG_TEGRA_UART_ODMDATA)) + base = tegra20_get_debuguart_base(); + if (IS_ENABLED(CONFIG_TEGRA_UART_A)) + base = TEGRA_UARTA_BASE; + if (IS_ENABLED(CONFIG_TEGRA_UART_B)) + base = TEGRA_UARTB_BASE; + if (IS_ENABLED(CONFIG_TEGRA_UART_C)) + base = TEGRA_UARTC_BASE; + if (IS_ENABLED(CONFIG_TEGRA_UART_D)) + base = TEGRA_UARTD_BASE; + if (IS_ENABLED(CONFIG_TEGRA_UART_E)) + base = TEGRA_UARTE_BASE; + + if (!base) + return -ENODEV; + + add_ns16550_device(DEVICE_ID_DYNAMIC, base, 8 << debug_uart.shift, + IORESOURCE_MEM_8BIT, &debug_uart); + + return 0; +} +console_initcall(tegra20_add_debug_console); + +static int tegra20_mem_init(void) +{ + arm_add_mem_device("ram0", 0x0, tegra20_get_ramsize()); + + return 0; +} +mem_initcall(tegra20_mem_init); diff --git a/arch/arm/mach-tegra/tegra_avp_init.c b/arch/arm/mach-tegra/tegra_avp_init.c new file mode 100644 index 0000000..4823733 --- /dev/null +++ b/arch/arm/mach-tegra/tegra_avp_init.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * Partly based on code (C) Copyright 2010-2011 + * NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include + +static inline void tegra_cpu_lowlevel_setup(void) +{ + uint32_t r; + + /* set the cpu to SVC32 mode */ + __asm__ __volatile__("mrs %0, cpsr":"=r"(r)); + r &= ~0x1f; + r |= 0xd3; + __asm__ __volatile__("msr cpsr, %0" : : "r"(r)); +} + +/* instruct the PMIC to enable the CPU power rail */ +static void enable_maincomplex_powerrail(void) +{ + u32 reg; + + reg = readl(TEGRA_PMC_BASE + PMC_CNTRL); + reg |= PMC_CNTRL_CPUPWRREQ_OE; + writel(reg, TEGRA_PMC_BASE); +} + +/* put every core in the main CPU complex into reset state */ +static void assert_maincomplex_reset(int num_cores) +{ + u32 mask = 0; + int i; + + for (i = 0; i < num_cores; i++) + mask |= 0x1111 << i; + + writel(mask, TEGRA_CLK_RESET_BASE + CRC_RST_CPU_CMPLX_SET); + writel(CRC_RST_DEV_L_SET_CPU, TEGRA_CLK_RESET_BASE + CRC_RST_DEV_L_SET); +} + +/* release reset state of the first core of the main CPU complex */ +static void deassert_cpu0_reset(void) +{ + writel(0x1111, TEGRA_CLK_RESET_BASE + CRC_RST_CPU_CMPLX_CLR); + writel(CRC_RST_DEV_L_CLR_CPU, TEGRA_CLK_RESET_BASE + CRC_RST_DEV_L_CLR); +} + +/* stop all internal and external clocks to the main CPU complex */ +static void stop_maincomplex_clocks(int num_cores) +{ + u32 reg; + int i; + + reg = readl(TEGRA_CLK_RESET_BASE + CRC_CLK_CPU_CMPLX); + for (i = 0; i < num_cores; i++) + reg |= 0x1 << (8 + i); + writel(reg, TEGRA_CLK_RESET_BASE + CRC_CLK_CPU_CMPLX); + + reg = readl(TEGRA_CLK_RESET_BASE + CRC_CLK_OUT_ENB_L); + reg &= ~CRC_CLK_OUT_ENB_L_CPU; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_CLK_OUT_ENB_L); +} + +struct pll_config { + u16 divn; + u16 divm; + u16 divp; + u16 cpcon; +}; + +static struct pll_config pllx_config_table[][4] = { + { + {1000, 13, 0, 12}, /* OSC 13.0 MHz */ + {625, 12, 0, 8 }, /* OSC 19.2 MHz */ + {1000, 12, 0, 12}, /* OSC 12.0 MHz */ + {1000, 26, 0, 12}, /* OSC 26.0 MHz */ + }, /* TEGRA 20 */ +}; + +static void init_pllx(void) +{ + struct pll_config *conf; + enum tegra_chiptype chiptype; + u8 osc_freq; + u32 reg; + + /* If PLLX is already enabled, just return */ + if (readl(TEGRA_CLK_RESET_BASE + CRC_PLLX_BASE) & CRC_PLLX_BASE_ENABLE) + return; + + chiptype = tegra_get_chiptype(); + if (chiptype < 0) + BUG(); + + osc_freq = (readl(TEGRA_CLK_RESET_BASE + CRC_OSC_CTRL) & + CRC_OSC_CTRL_OSC_FREQ_MASK) >> CRC_OSC_CTRL_OSC_FREQ_SHIFT; + + conf = &pllx_config_table[chiptype][osc_freq]; + + /* set PLL bypass and frequency parameters */ + reg = CRC_PLLX_BASE_BYPASS; + reg |= (conf->divm << CRC_PLLX_BASE_DIVM_SHIFT) & + CRC_PLLX_BASE_DIVM_MASK; + reg |= (conf->divn << CRC_PLLX_BASE_DIVN_SHIFT) & + CRC_PLLX_BASE_DIVN_MASK; + reg |= (conf->divp << CRC_PLLX_BASE_DIVP_SHIFT) & + CRC_PLLX_BASE_DIVP_MASK; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_PLLX_BASE); + + /* set chargepump parameters */ + reg = (conf->cpcon << CRC_PLLX_MISC_CPCON_SHIFT) & + CRC_PLLX_MISC_CPCON_MASK; + if (conf->divn > 600) + reg |= CRC_PLLX_MISC_DCCON; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_PLLX_MISC); + + /* enable PLL and disable bypass */ + reg = readl(TEGRA_CLK_RESET_BASE + CRC_PLLX_BASE); + reg |= CRC_PLLX_BASE_ENABLE; + reg &= ~CRC_PLLX_BASE_BYPASS; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_PLLX_BASE); + + /* enable PLL lock */ + reg = readl(TEGRA_CLK_RESET_BASE + CRC_PLLX_MISC); + reg |= CRC_PLLX_MISC_LOCK_ENABLE; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_PLLX_MISC); +} + +/* start internal and external clocks to core 0 of the main CPU complex */ +static void start_cpu0_clocks(void) +{ + u32 reg; + + /* setup PLLX */ + init_pllx(); + + /* setup super CLK */ + writel(CRC_SCLK_BURST_POLICY_SYS_STATE_RUN << + CRC_SCLK_BURST_POLICY_SYS_STATE_SHIFT, + TEGRA_CLK_RESET_BASE + CRC_SCLK_BURST_POLICY); + writel(CRC_SUPER_SDIV_ENB, TEGRA_CLK_RESET_BASE + CRC_SUPER_SCLK_DIV); + + /* deassert clock stop for cpu 0 */ + reg = readl(TEGRA_CLK_RESET_BASE + CRC_CLK_CPU_CMPLX); + reg &= ~CRC_CLK_CPU_CMPLX_CPU0_CLK_STP; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_CLK_CPU_CMPLX); + + /* enable main CPU complex clock */ + reg = readl(TEGRA_CLK_RESET_BASE + CRC_CLK_OUT_ENB_L); + reg |= CRC_CLK_OUT_ENB_L_CPU; + writel(reg, TEGRA_CLK_RESET_BASE + CRC_CLK_OUT_ENB_L); +} + +static void maincomplex_powerup(void) +{ + u32 reg; + + if (!(readl(TEGRA_PMC_BASE + PMC_PWRGATE_STATUS) & + PMC_PWRGATE_STATUS_CPU)) { + writel(PMC_PWRGATE_TOGGLE_START | PMC_PWRGATE_TOGGLE_PARTID_CPU, + TEGRA_PMC_BASE + PMC_PWRGATE_TOGGLE); + + while (!(readl(TEGRA_PMC_BASE + PMC_PWRGATE_STATUS) & + PMC_PWRGATE_STATUS_CPU)); + + reg = readl(TEGRA_PMC_BASE + PMC_REMOVE_CLAMPING_CMD); + reg |= PMC_REMOVE_CLAMPING_CMD_CPU; + writel(reg, TEGRA_PMC_BASE + PMC_REMOVE_CLAMPING_CMD); + } +} +void barebox_arm_reset_vector(void) +{ + int num_cores; + + /* minimal initialization, OK for both ARMv4 and ARMv7 */ + tegra_cpu_lowlevel_setup(); + + /* + * If we are already running on the main CPU complex jump straight + * to the maincomplex entry point. + */ + if (tegra_cpu_is_maincomplex()) + tegra_maincomplex_entry(); + + /* get the number of cores in the main CPU complex of the current SoC */ + num_cores = tegra_get_num_cores(); + if (!num_cores) + BUG(); + + /* bring down main CPU complex (this may be a warm boot) */ + enable_maincomplex_powerrail(); + assert_maincomplex_reset(num_cores); + stop_maincomplex_clocks(num_cores); + + /* set start address for the main CPU complex processors */ + writel(barebox_arm_head, TEGRA_EXCEPTION_VECTORS_BASE + 0x100); + + /* bring up main CPU complex */ + start_cpu0_clocks(); + maincomplex_powerup(); + deassert_cpu0_reset(); + + /* assert AVP reset to stop execution here */ + writel(CRC_RST_DEV_L_SET_COP, TEGRA_CLK_RESET_BASE + CRC_RST_DEV_L_SET); + + unreachable(); +} diff --git a/arch/arm/mach-tegra/tegra_maincomplex_init.c b/arch/arm/mach-tegra/tegra_maincomplex_init.c new file mode 100644 index 0000000..dea9c91 --- /dev/null +++ b/arch/arm/mach-tegra/tegra_maincomplex_init.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include + +void tegra_maincomplex_entry(void) +{ + uint32_t rambase, ramsize; + + arm_cpu_lowlevel_init(); + + switch (tegra_get_chiptype()) { + case TEGRA20: + rambase = 0x0; + ramsize = tegra20_get_ramsize(); + break; + default: + /* If we don't know the chiptype, better bail out */ + BUG(); + } + + /* + * The standard load address for Tegra systems is 0x10800 which means + * the barebox binary will always be below the malloc area for all + * reasonable malloc area sizes. We offset the RAM base address by 8MB + * to pretend barebox is in another bank. + */ + barebox_arm_entry(rambase + SZ_8M, ramsize - SZ_8M, 0); +} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 74a4baa..e976db4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -39,6 +39,13 @@ config GPIO_STMPE depends on MFD_STMPE bool "STMPE GPIO Expander" + +config GPIO_TEGRA + bool "GPIO support for the Tegra SoCs" + depends on ARCH_TEGRA + help + Say yes here to include the driver for the GPIO controller found on the + Tegra line of SoCs. endmenu endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 205d553..c1ec5bf 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_GPIO_IMX) += gpio-imx.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o +obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c new file mode 100644 index 0000000..1e00f5e --- /dev/null +++ b/drivers/gpio/gpio-tegra.c @@ -0,0 +1,214 @@ +/* * + * Copyright (C) 2010 Erik Gilling , Google, Inc + * Copyright (C) 2013 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include + +#define GPIO_BANK(x) ((x) >> 5) +#define GPIO_PORT(x) (((x) >> 3) & 0x3) +#define GPIO_BIT(x) ((x) & 0x7) + +#define GPIO_REG(x) (GPIO_BANK(x) * config->bank_stride + \ + GPIO_PORT(x) * 4) + +#define GPIO_CNF(x) (GPIO_REG(x) + 0x00) +#define GPIO_OE(x) (GPIO_REG(x) + 0x10) +#define GPIO_OUT(x) (GPIO_REG(x) + 0x20) +#define GPIO_IN(x) (GPIO_REG(x) + 0x30) +#define GPIO_INT_ENB(x) (GPIO_REG(x) + 0x50) + +#define GPIO_MSK_CNF(x) (GPIO_REG(x) + config->upper_offset + 0x00) +#define GPIO_MSK_OE(x) (GPIO_REG(x) + config->upper_offset + 0x10) +#define GPIO_MSK_OUT(x) (GPIO_REG(x) + config->upper_offset + 0X20) + +struct tegra_gpio_bank { + int bank; + int irq; +}; + +struct tegra_gpio_soc_config { + u32 bank_stride; + u32 upper_offset; + u32 bank_count; +}; + +static void __iomem *gpio_base; +static struct tegra_gpio_soc_config *config; + +static inline void tegra_gpio_writel(u32 val, u32 reg) +{ + writel(val, gpio_base + reg); +} + +static inline u32 tegra_gpio_readl(u32 reg) +{ + return readl(gpio_base + reg); +} + +static int tegra_gpio_compose(int bank, int port, int bit) +{ + return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); +} + +static void tegra_gpio_mask_write(u32 reg, int gpio, int value) +{ + u32 val; + + val = 0x100 << GPIO_BIT(gpio); + if (value) + val |= 1 << GPIO_BIT(gpio); + tegra_gpio_writel(val, reg); +} + +static void tegra_gpio_enable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1); +} + +static void tegra_gpio_disable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0); +} + +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return 0; +} + +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_disable(offset); +} + +static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + tegra_gpio_mask_write(GPIO_MSK_OUT(offset), offset, value); +} + +static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + /* If gpio is in output mode then read from the out value */ + if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1) { + printf("GPIO output mode\n"); + return (tegra_gpio_readl(GPIO_OUT(offset)) >> + GPIO_BIT(offset)) & 0x1; + } + + printf("GPIO input mode\n"); + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; +} + +static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0); + tegra_gpio_enable(offset); + return 0; +} + +static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + tegra_gpio_set(chip, offset, value); + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1); + tegra_gpio_enable(offset); + return 0; +} + +static struct gpio_ops tegra_gpio_ops = { + .request = tegra_gpio_request, + .free = tegra_gpio_free, + .direction_input = tegra_gpio_direction_input, + .direction_output = tegra_gpio_direction_output, + .get = tegra_gpio_get, + .set = tegra_gpio_set, +}; + +static struct gpio_chip tegra_gpio_chip = { + .ops = &tegra_gpio_ops, + .base = 0, +}; + +static int tegra_gpio_probe(struct device_d *dev) +{ + int i, j, ret; + + ret = dev_get_drvdata(dev, (unsigned long *)&config); + if (ret) { + dev_err(dev, "dev_get_drvdata failed: %d\n", ret); + return ret; + } + + gpio_base = dev_request_mem_region(dev, 0); + if (!gpio_base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + for (i = 0; i < config->bank_count; i++) { + for (j = 0; j < 4; j++) { + int gpio = tegra_gpio_compose(i, j, 0); + tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio)); + } + } + + tegra_gpio_chip.ngpio = config->bank_count * 32; + tegra_gpio_chip.dev = dev; + + gpiochip_add(&tegra_gpio_chip); + + return 0; +} + +static struct tegra_gpio_soc_config tegra20_gpio_config = { + .bank_stride = 0x80, + .upper_offset = 0x800, + .bank_count = 7, +}; + +static struct platform_device_id tegra_gpio_ids[] = { + { + .name = "tegra20-gpio", + .driver_data = (unsigned long)&tegra20_gpio_config, + }, { + /* sentinel */ + }, +}; + +static __maybe_unused struct of_device_id tegra_gpio_dt_ids[] = { + { + .compatible = "nvidia,tegra20-gpio", + .data = (unsigned long)&tegra20_gpio_config + }, { + /* sentinel */ + }, +}; + +static struct driver_d tegra_gpio_driver = { + .name = "tegra-gpio", + .id_table = tegra_gpio_ids, + .of_compatible = DRV_OF_COMPAT(tegra_gpio_dt_ids), + .probe = tegra_gpio_probe, +}; + +static int __init tegra_gpio_init(void) +{ + return platform_driver_register(&tegra_gpio_driver); +} +coredevice_initcall(tegra_gpio_init);