summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/Kconfig15
-rw-r--r--arch/arm/mach-tegra/Makefile14
-rw-r--r--arch/arm/mach-tegra/arm64-mmu.c6
-rw-r--r--arch/arm/mach-tegra/board186.c18
-rw-r--r--arch/arm/mach-tegra/board2.c26
-rw-r--r--arch/arm/mach-tegra/clock.c2
-rw-r--r--arch/arm/mach-tegra/cpu.h2
-rw-r--r--arch/arm/mach-tegra/dt-setup.c34
-rw-r--r--arch/arm/mach-tegra/ivc.c553
-rw-r--r--arch/arm/mach-tegra/psci.S16
-rw-r--r--arch/arm/mach-tegra/tegra186/Makefile5
-rw-r--r--arch/arm/mach-tegra/tegra186/nvtboot_ll.S20
-rw-r--r--arch/arm/mach-tegra/tegra186/nvtboot_mem.c88
-rw-r--r--arch/arm/mach-tegra/tegra20/Makefile3
14 files changed, 737 insertions, 65 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index f4affa5512..1eaf406272 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -1,5 +1,13 @@
if TEGRA
+config TEGRA_IVC
+ bool "Tegra IVC protocol"
+ help
+ IVC (Inter-VM Communication) protocol is a Tegra-specific IPC
+ (Inter Processor Communication) framework. Within the context of
+ U-Boot, it is typically used for communication between the main CPU
+ and various auxiliary processors.
+
config TEGRA_COMMON
bool "Tegra common options"
select DM
@@ -56,10 +64,17 @@ config TEGRA210
config TEGRA186
bool "Tegra186 family"
+ select CLK
select DM_MAILBOX
+ select DM_RESET
+ select MISC
+ select TEGRA186_BPMP
+ select TEGRA186_CLOCK
select TEGRA186_GPIO
+ select TEGRA186_RESET
select TEGRA_ARMV8_COMMON
select TEGRA_HSP
+ select TEGRA_IVC
endchoice
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 12ee1cd749..b978fec075 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -15,23 +15,25 @@ else
obj-$(CONFIG_CMD_ENTERRCM) += cmd_enterrcm.o
endif
-obj-$(CONFIG_ARM64) += arm64-mmu.o
obj-y += ap.o
obj-y += board.o board2.o
obj-y += cache.o
obj-y += clock.o
-obj-y += lowlevel_init.o
obj-y += pinmux-common.o
obj-y += powergate.o
obj-y += xusb-padctl-dummy.o
-obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o
-obj-$(CONFIG_TEGRA_GPU) += gpu.o
-obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o
+endif
+obj-$(CONFIG_ARM64) += arm64-mmu.o
+obj-y += dt-setup.o
+obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o
+obj-$(CONFIG_TEGRA_GPU) += gpu.o
+obj-$(CONFIG_TEGRA_IVC) += ivc.o
+obj-y += lowlevel_init.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_ARMV7_PSCI) += psci.o
endif
-endif
+obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o
obj-$(CONFIG_TEGRA20) += tegra20/
obj-$(CONFIG_TEGRA30) += tegra30/
diff --git a/arch/arm/mach-tegra/arm64-mmu.c b/arch/arm/mach-tegra/arm64-mmu.c
index 501c4f00c4..7b1d258ed8 100644
--- a/arch/arm/mach-tegra/arm64-mmu.c
+++ b/arch/arm/mach-tegra/arm64-mmu.c
@@ -14,13 +14,15 @@
static struct mm_region tegra_mem_map[] = {
{
- .base = 0x0UL,
+ .virt = 0x0UL,
+ .phys = 0x0UL,
.size = 0x80000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
- .base = 0x80000000UL,
+ .virt = 0x80000000UL,
+ .phys = 0x80000000UL,
.size = 0xff80000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_INNER_SHARE
diff --git a/arch/arm/mach-tegra/board186.c b/arch/arm/mach-tegra/board186.c
index f4b6152a79..1b9799fd80 100644
--- a/arch/arm/mach-tegra/board186.c
+++ b/arch/arm/mach-tegra/board186.c
@@ -11,20 +11,19 @@
DECLARE_GLOBAL_DATA_PTR;
-int dram_init(void)
+int board_early_init_f(void)
{
- gd->ram_size = (1.5 * 1024 * 1024 * 1024);
return 0;
}
-int board_early_init_f(void)
+__weak int tegra_board_init(void)
{
return 0;
}
int board_init(void)
{
- return 0;
+ return tegra_board_init();
}
int board_late_init(void)
@@ -32,12 +31,6 @@ int board_late_init(void)
return 0;
}
-void dram_init_banksize(void)
-{
- gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
- gd->bd->bi_dram[0].size = gd->ram_size;
-}
-
void pad_init_mmc(struct mmc_host *host)
{
}
@@ -48,8 +41,3 @@ int board_mmc_init(bd_t *bd)
return 0;
}
-
-int ft_system_setup(void *blob, bd_t *bd)
-{
- return 0;
-}
diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c
index 141d6e1cb5..9158ace44c 100644
--- a/arch/arm/mach-tegra/board2.c
+++ b/arch/arm/mach-tegra/board2.c
@@ -397,29 +397,3 @@ ulong board_get_usable_ram_top(ulong total_size)
{
return CONFIG_SYS_SDRAM_BASE + usable_ram_size_below_4g();
}
-
-/*
- * This function is called right before the kernel is booted. "blob" is the
- * device tree that will be passed to the kernel.
- */
-int ft_system_setup(void *blob, bd_t *bd)
-{
- const char *gpu_compats[] = {
-#if defined(CONFIG_TEGRA124)
- "nvidia,gk20a",
-#endif
-#if defined(CONFIG_TEGRA210)
- "nvidia,gm20b",
-#endif
- };
- int i, ret;
-
- /* Enable GPU node if GPU setup has been performed */
- for (i = 0; i < ARRAY_SIZE(gpu_compats); i++) {
- ret = tegra_gpu_enable_node(blob, gpu_compats[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index c50d56dc88..36eabc8f57 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -510,7 +510,7 @@ unsigned clock_get_rate(enum clock_id clkid)
* @param p post divider(DIVP)
* @param cpcon base PLL charge pump(CPCON)
* @return 0 if ok, -1 on error (the requested PLL is incorrect and cannot
- * be overriden), 1 if PLL is already correct
+ * be overridden), 1 if PLL is already correct
*/
int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon)
{
diff --git a/arch/arm/mach-tegra/cpu.h b/arch/arm/mach-tegra/cpu.h
index 3f38969a44..1154f8b37e 100644
--- a/arch/arm/mach-tegra/cpu.h
+++ b/arch/arm/mach-tegra/cpu.h
@@ -16,7 +16,7 @@
#elif defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA114) || \
defined(CONFIG_TEGRA124) || defined(CONFIG_TEGRA210)
#define NVBL_PLLP_KHZ 408000
-#define CSITE_KHZ 204000
+#define CSITE_KHZ 136000
#else
#error "Unknown Tegra chip!"
#endif
diff --git a/arch/arm/mach-tegra/dt-setup.c b/arch/arm/mach-tegra/dt-setup.c
new file mode 100644
index 0000000000..f44d9cb04d
--- /dev/null
+++ b/arch/arm/mach-tegra/dt-setup.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch-tegra/gpu.h>
+
+/*
+ * This function is called right before the kernel is booted. "blob" is the
+ * device tree that will be passed to the kernel.
+ */
+int ft_system_setup(void *blob, bd_t *bd)
+{
+ const char *gpu_compats[] = {
+#if defined(CONFIG_TEGRA124)
+ "nvidia,gk20a",
+#endif
+#if defined(CONFIG_TEGRA210)
+ "nvidia,gm20b",
+#endif
+ };
+ int i, ret;
+
+ /* Enable GPU node if GPU setup has been performed */
+ for (i = 0; i < ARRAY_SIZE(gpu_compats); i++) {
+ ret = tegra_gpu_enable_node(blob, gpu_compats[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/ivc.c b/arch/arm/mach-tegra/ivc.c
new file mode 100644
index 0000000000..cf6626fb12
--- /dev/null
+++ b/arch/arm/mach-tegra/ivc.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch-tegra/ivc.h>
+
+#define TEGRA_IVC_ALIGN 64
+
+/*
+ * IVC channel reset protocol.
+ *
+ * Each end uses its tx_channel.state to indicate its synchronization state.
+ */
+enum ivc_state {
+ /*
+ * This value is zero for backwards compatibility with services that
+ * assume channels to be initially zeroed. Such channels are in an
+ * initially valid state, but cannot be asynchronously reset, and must
+ * maintain a valid state at all times.
+ *
+ * The transmitting end can enter the established state from the sync or
+ * ack state when it observes the receiving endpoint in the ack or
+ * established state, indicating that has cleared the counters in our
+ * rx_channel.
+ */
+ ivc_state_established = 0,
+
+ /*
+ * If an endpoint is observed in the sync state, the remote endpoint is
+ * allowed to clear the counters it owns asynchronously with respect to
+ * the current endpoint. Therefore, the current endpoint is no longer
+ * allowed to communicate.
+ */
+ ivc_state_sync,
+
+ /*
+ * When the transmitting end observes the receiving end in the sync
+ * state, it can clear the w_count and r_count and transition to the ack
+ * state. If the remote endpoint observes us in the ack state, it can
+ * return to the established state once it has cleared its counters.
+ */
+ ivc_state_ack
+};
+
+/*
+ * This structure is divided into two-cache aligned parts, the first is only
+ * written through the tx_channel pointer, while the second is only written
+ * through the rx_channel pointer. This delineates ownership of the cache lines,
+ * which is critical to performance and necessary in non-cache coherent
+ * implementations.
+ */
+struct tegra_ivc_channel_header {
+ union {
+ /* fields owned by the transmitting end */
+ struct {
+ uint32_t w_count;
+ uint32_t state;
+ };
+ uint8_t w_align[TEGRA_IVC_ALIGN];
+ };
+ union {
+ /* fields owned by the receiving end */
+ uint32_t r_count;
+ uint8_t r_align[TEGRA_IVC_ALIGN];
+ };
+};
+
+static inline void tegra_ivc_invalidate_counter(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *h,
+ ulong offset)
+{
+ ulong base = ((ulong)h) + offset;
+ invalidate_dcache_range(base, base + TEGRA_IVC_ALIGN);
+}
+
+static inline void tegra_ivc_flush_counter(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *h,
+ ulong offset)
+{
+ ulong base = ((ulong)h) + offset;
+ flush_dcache_range(base, base + TEGRA_IVC_ALIGN);
+}
+
+static inline ulong tegra_ivc_frame_addr(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *h,
+ uint32_t frame)
+{
+ BUG_ON(frame >= ivc->nframes);
+
+ return ((ulong)h) + sizeof(struct tegra_ivc_channel_header) +
+ (ivc->frame_size * frame);
+}
+
+static inline void *tegra_ivc_frame_pointer(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *ch,
+ uint32_t frame)
+{
+ return (void *)tegra_ivc_frame_addr(ivc, ch, frame);
+}
+
+static inline void tegra_ivc_invalidate_frame(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *h,
+ unsigned frame)
+{
+ ulong base = tegra_ivc_frame_addr(ivc, h, frame);
+ invalidate_dcache_range(base, base + ivc->frame_size);
+}
+
+static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *h,
+ unsigned frame)
+{
+ ulong base = tegra_ivc_frame_addr(ivc, h, frame);
+ flush_dcache_range(base, base + ivc->frame_size);
+}
+
+static inline int tegra_ivc_channel_empty(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *ch)
+{
+ /*
+ * This function performs multiple checks on the same values with
+ * security implications, so create snapshots with ACCESS_ONCE() to
+ * ensure that these checks use the same values.
+ */
+ uint32_t w_count = ACCESS_ONCE(ch->w_count);
+ uint32_t r_count = ACCESS_ONCE(ch->r_count);
+
+ /*
+ * Perform an over-full check to prevent denial of service attacks where
+ * a server could be easily fooled into believing that there's an
+ * extremely large number of frames ready, since receivers are not
+ * expected to check for full or over-full conditions.
+ *
+ * Although the channel isn't empty, this is an invalid case caused by
+ * a potentially malicious peer, so returning empty is safer, because it
+ * gives the impression that the channel has gone silent.
+ */
+ if (w_count - r_count > ivc->nframes)
+ return 1;
+
+ return w_count == r_count;
+}
+
+static inline int tegra_ivc_channel_full(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *ch)
+{
+ /*
+ * Invalid cases where the counters indicate that the queue is over
+ * capacity also appear full.
+ */
+ return (ACCESS_ONCE(ch->w_count) - ACCESS_ONCE(ch->r_count)) >=
+ ivc->nframes;
+}
+
+static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
+{
+ ACCESS_ONCE(ivc->rx_channel->r_count) =
+ ACCESS_ONCE(ivc->rx_channel->r_count) + 1;
+
+ if (ivc->r_pos == ivc->nframes - 1)
+ ivc->r_pos = 0;
+ else
+ ivc->r_pos++;
+}
+
+static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
+{
+ ACCESS_ONCE(ivc->tx_channel->w_count) =
+ ACCESS_ONCE(ivc->tx_channel->w_count) + 1;
+
+ if (ivc->w_pos == ivc->nframes - 1)
+ ivc->w_pos = 0;
+ else
+ ivc->w_pos++;
+}
+
+static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
+{
+ ulong offset;
+
+ /*
+ * tx_channel->state is set locally, so it is not synchronized with
+ * state from the remote peer. The remote peer cannot reset its
+ * transmit counters until we've acknowledged its synchronization
+ * request, so no additional synchronization is required because an
+ * asynchronous transition of rx_channel->state to ivc_state_ack is not
+ * allowed.
+ */
+ if (ivc->tx_channel->state != ivc_state_established)
+ return -ECONNRESET;
+
+ /*
+ * Avoid unnecessary invalidations when performing repeated accesses to
+ * an IVC channel by checking the old queue pointers first.
+ * Synchronization is only necessary when these pointers indicate empty
+ * or full.
+ */
+ if (!tegra_ivc_channel_empty(ivc, ivc->rx_channel))
+ return 0;
+
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
+ return tegra_ivc_channel_empty(ivc, ivc->rx_channel) ? -ENOMEM : 0;
+}
+
+static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
+{
+ ulong offset;
+
+ if (ivc->tx_channel->state != ivc_state_established)
+ return -ECONNRESET;
+
+ if (!tegra_ivc_channel_full(ivc, ivc->tx_channel))
+ return 0;
+
+ offset = offsetof(struct tegra_ivc_channel_header, r_count);
+ tegra_ivc_invalidate_counter(ivc, ivc->tx_channel, offset);
+ return tegra_ivc_channel_full(ivc, ivc->tx_channel) ? -ENOMEM : 0;
+}
+
+static inline uint32_t tegra_ivc_channel_avail_count(struct tegra_ivc *ivc,
+ struct tegra_ivc_channel_header *ch)
+{
+ /*
+ * This function isn't expected to be used in scenarios where an
+ * over-full situation can lead to denial of service attacks. See the
+ * comment in tegra_ivc_channel_empty() for an explanation about
+ * special over-full considerations.
+ */
+ return ACCESS_ONCE(ch->w_count) - ACCESS_ONCE(ch->r_count);
+}
+
+int tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc, void **frame)
+{
+ int result = tegra_ivc_check_read(ivc);
+ if (result < 0)
+ return result;
+
+ /*
+ * Order observation of w_pos potentially indicating new data before
+ * data read.
+ */
+ mb();
+
+ tegra_ivc_invalidate_frame(ivc, ivc->rx_channel, ivc->r_pos);
+ *frame = tegra_ivc_frame_pointer(ivc, ivc->rx_channel, ivc->r_pos);
+
+ return 0;
+}
+
+int tegra_ivc_read_advance(struct tegra_ivc *ivc)
+{
+ ulong offset;
+ int result;
+
+ /*
+ * No read barriers or synchronization here: the caller is expected to
+ * have already observed the channel non-empty. This check is just to
+ * catch programming errors.
+ */
+ result = tegra_ivc_check_read(ivc);
+ if (result)
+ return result;
+
+ tegra_ivc_advance_rx(ivc);
+ offset = offsetof(struct tegra_ivc_channel_header, r_count);
+ tegra_ivc_flush_counter(ivc, ivc->rx_channel, offset);
+
+ /*
+ * Ensure our write to r_pos occurs before our read from w_pos.
+ */
+ mb();
+
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
+
+ if (tegra_ivc_channel_avail_count(ivc, ivc->rx_channel) ==
+ ivc->nframes - 1)
+ ivc->notify(ivc);
+
+ return 0;
+}
+
+int tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc, void **frame)
+{
+ int result = tegra_ivc_check_write(ivc);
+ if (result)
+ return result;
+
+ *frame = tegra_ivc_frame_pointer(ivc, ivc->tx_channel, ivc->w_pos);
+
+ return 0;
+}
+
+int tegra_ivc_write_advance(struct tegra_ivc *ivc)
+{
+ ulong offset;
+ int result;
+
+ result = tegra_ivc_check_write(ivc);
+ if (result)
+ return result;
+
+ tegra_ivc_flush_frame(ivc, ivc->tx_channel, ivc->w_pos);
+
+ /*
+ * Order any possible stores to the frame before update of w_pos.
+ */
+ mb();
+
+ tegra_ivc_advance_tx(ivc);
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
+
+ /*
+ * Ensure our write to w_pos occurs before our read from r_pos.
+ */
+ mb();
+
+ offset = offsetof(struct tegra_ivc_channel_header, r_count);
+ tegra_ivc_invalidate_counter(ivc, ivc->tx_channel, offset);
+
+ if (tegra_ivc_channel_avail_count(ivc, ivc->tx_channel) == 1)
+ ivc->notify(ivc);
+
+ return 0;
+}
+
+/*
+ * ===============================================================
+ * IVC State Transition Table - see tegra_ivc_channel_notified()
+ * ===============================================================
+ *
+ * local remote action
+ * ----- ------ -----------------------------------
+ * SYNC EST <none>
+ * SYNC ACK reset counters; move to EST; notify
+ * SYNC SYNC reset counters; move to ACK; notify
+ * ACK EST move to EST; notify
+ * ACK ACK move to EST; notify
+ * ACK SYNC reset counters; move to ACK; notify
+ * EST EST <none>
+ * EST ACK <none>
+ * EST SYNC reset counters; move to ACK; notify
+ *
+ * ===============================================================
+ */
+int tegra_ivc_channel_notified(struct tegra_ivc *ivc)
+{
+ ulong offset;
+ enum ivc_state peer_state;
+
+ /* Copy the receiver's state out of shared memory. */
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
+ peer_state = ACCESS_ONCE(ivc->rx_channel->state);
+
+ if (peer_state == ivc_state_sync) {
+ /*
+ * Order observation of ivc_state_sync before stores clearing
+ * tx_channel.
+ */
+ mb();
+
+ /*
+ * Reset tx_channel counters. The remote end is in the SYNC
+ * state and won't make progress until we change our state,
+ * so the counters are not in use at this time.
+ */
+ ivc->tx_channel->w_count = 0;
+ ivc->rx_channel->r_count = 0;
+
+ ivc->w_pos = 0;
+ ivc->r_pos = 0;
+
+ /*
+ * Ensure that counters appear cleared before new state can be
+ * observed.
+ */
+ mb();
+
+ /*
+ * Move to ACK state. We have just cleared our counters, so it
+ * is now safe for the remote end to start using these values.
+ */
+ ivc->tx_channel->state = ivc_state_ack;
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
+
+ /*
+ * Notify remote end to observe state transition.
+ */
+ ivc->notify(ivc);
+ } else if (ivc->tx_channel->state == ivc_state_sync &&
+ peer_state == ivc_state_ack) {
+ /*
+ * Order observation of ivc_state_sync before stores clearing
+ * tx_channel.
+ */
+ mb();
+
+ /*
+ * Reset tx_channel counters. The remote end is in the ACK
+ * state and won't make progress until we change our state,
+ * so the counters are not in use at this time.
+ */
+ ivc->tx_channel->w_count = 0;
+ ivc->rx_channel->r_count = 0;
+
+ ivc->w_pos = 0;
+ ivc->r_pos = 0;
+
+ /*
+ * Ensure that counters appear cleared before new state can be
+ * observed.
+ */
+ mb();
+
+ /*
+ * Move to ESTABLISHED state. We know that the remote end has
+ * already cleared its counters, so it is safe to start
+ * writing/reading on this channel.
+ */
+ ivc->tx_channel->state = ivc_state_established;
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
+
+ /*
+ * Notify remote end to observe state transition.
+ */
+ ivc->notify(ivc);
+ } else if (ivc->tx_channel->state == ivc_state_ack) {
+ /*
+ * At this point, we have observed the peer to be in either
+ * the ACK or ESTABLISHED state. Next, order observation of
+ * peer state before storing to tx_channel.
+ */
+ mb();
+
+ /*
+ * Move to ESTABLISHED state. We know that we have previously
+ * cleared our counters, and we know that the remote end has
+ * cleared its counters, so it is safe to start writing/reading
+ * on this channel.
+ */
+ ivc->tx_channel->state = ivc_state_established;
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
+
+ /*
+ * Notify remote end to observe state transition.
+ */
+ ivc->notify(ivc);
+ } else {
+ /*
+ * There is no need to handle any further action. Either the
+ * channel is already fully established, or we are waiting for
+ * the remote end to catch up with our current state. Refer
+ * to the diagram in "IVC State Transition Table" above.
+ */
+ }
+
+ if (ivc->tx_channel->state != ivc_state_established)
+ return -EAGAIN;
+
+ return 0;
+}
+
+void tegra_ivc_channel_reset(struct tegra_ivc *ivc)
+{
+ ulong offset;
+
+ ivc->tx_channel->state = ivc_state_sync;
+ offset = offsetof(struct tegra_ivc_channel_header, w_count);
+ tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
+ ivc->notify(ivc);
+}
+
+static int check_ivc_params(ulong qbase1, ulong qbase2, uint32_t nframes,
+ uint32_t frame_size)
+{
+ int ret = 0;
+
+ BUG_ON(offsetof(struct tegra_ivc_channel_header, w_count) &
+ (TEGRA_IVC_ALIGN - 1));
+ BUG_ON(offsetof(struct tegra_ivc_channel_header, r_count) &
+ (TEGRA_IVC_ALIGN - 1));
+ BUG_ON(sizeof(struct tegra_ivc_channel_header) &
+ (TEGRA_IVC_ALIGN - 1));
+
+ if ((uint64_t)nframes * (uint64_t)frame_size >= 0x100000000) {
+ error("tegra_ivc: nframes * frame_size overflows\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The headers must at least be aligned enough for counters
+ * to be accessed atomically.
+ */
+ if ((qbase1 & (TEGRA_IVC_ALIGN - 1)) ||
+ (qbase2 & (TEGRA_IVC_ALIGN - 1))) {
+ error("tegra_ivc: channel start not aligned\n");
+ return -EINVAL;
+ }
+
+ if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
+ error("tegra_ivc: frame size not adequately aligned\n");
+ return -EINVAL;
+ }
+
+ if (qbase1 < qbase2) {
+ if (qbase1 + frame_size * nframes > qbase2)
+ ret = -EINVAL;
+ } else {
+ if (qbase2 + frame_size * nframes > qbase1)
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ error("tegra_ivc: queue regions overlap\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int tegra_ivc_init(struct tegra_ivc *ivc, ulong rx_base, ulong tx_base,
+ uint32_t nframes, uint32_t frame_size,
+ void (*notify)(struct tegra_ivc *))
+{
+ int ret;
+
+ if (!ivc)
+ return -EINVAL;
+
+ ret = check_ivc_params(rx_base, tx_base, nframes, frame_size);
+ if (ret)
+ return ret;
+
+ ivc->rx_channel = (struct tegra_ivc_channel_header *)rx_base;
+ ivc->tx_channel = (struct tegra_ivc_channel_header *)tx_base;
+ ivc->w_pos = 0;
+ ivc->r_pos = 0;
+ ivc->nframes = nframes;
+ ivc->frame_size = frame_size;
+ ivc->notify = notify;
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S
index b836da1c0e..645d08fa0b 100644
--- a/arch/arm/mach-tegra/psci.S
+++ b/arch/arm/mach-tegra/psci.S
@@ -61,9 +61,6 @@ ENTRY(psci_arch_init)
ldrne r7, [r5]
mcrne p15, 0, r7, c14, c0, 0 @ write CNTFRQ to CPU1..3
- bl psci_get_cpu_stack_top @ stack top => r0
- mov sp, r0
-
bx r6
ENDPROC(psci_arch_init)
@@ -88,12 +85,13 @@ _loop: wfi
ENDPROC(psci_cpu_off)
ENTRY(psci_cpu_on)
- push {lr}
+ push {r4, r5, r6, lr}
+ mov r4, r1
mov r0, r1
- bl psci_get_cpu_stack_top @ get stack top of target CPU
- str r2, [r0] @ store target PC at stack top
- dsb
+ mov r1, r2
+ bl psci_save_target_pc @ store target PC
+ mov r1, r4
ldr r6, =TEGRA_RESET_EXCEPTION_VECTOR
ldr r5, =psci_cpu_entry
@@ -106,9 +104,7 @@ ENTRY(psci_cpu_on)
str r5, [r6, r2]
mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS
- pop {pc}
+ pop {r4, r5, r6, pc}
ENDPROC(psci_cpu_on)
- .globl psci_text_end
-psci_text_end:
.popsection
diff --git a/arch/arm/mach-tegra/tegra186/Makefile b/arch/arm/mach-tegra/tegra186/Makefile
index ce4610d8f8..033d6005fb 100644
--- a/arch/arm/mach-tegra/tegra186/Makefile
+++ b/arch/arm/mach-tegra/tegra186/Makefile
@@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: GPL-2.0
-obj-y += ../arm64-mmu.o
obj-y += ../board186.o
-obj-y += ../lowlevel_init.o
-obj-$(CONFIG_DISPLAY_CPUINFO) += ../sys_info.o
+obj-y += nvtboot_ll.o
+obj-y += nvtboot_mem.o
diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_ll.S b/arch/arm/mach-tegra/tegra186/nvtboot_ll.S
new file mode 100644
index 0000000000..1eab890958
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra186/nvtboot_ll.S
@@ -0,0 +1,20 @@
+/*
+ * Save nvtboot-related boot-time CPU state
+ *
+ * (C) Copyright 2015-2016 NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <linux/linkage.h>
+
+.globl nvtboot_boot_x0
+nvtboot_boot_x0:
+ .dword 0
+
+ENTRY(save_boot_params)
+ adr x8, nvtboot_boot_x0
+ str x0, [x8]
+ b save_boot_params_ret
+ENDPROC(save_boot_params)
diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c
new file mode 100644
index 0000000000..37dd8d4334
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdt_support.h>
+#include <fdtdec.h>
+#include <asm/arch/tegra.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+extern unsigned long nvtboot_boot_x0;
+
+/*
+ * A parsed version of /memory/reg from the DTB that is passed to U-Boot in x0.
+ *
+ * We only support up to two banks since that's all the binary bootloader
+ * ever sets. We assume bank 0 is RAM below 4G and bank 1 is RAM above 4G.
+ * This is all a fairly safe assumption, since the L4T kernel makes the same
+ * assumptions, so the bootloader is unlikely to change.
+ *
+ * This is written to before relocation, and hence cannot be in .bss, since
+ * .bss overlaps the DTB that's appended to the U-Boot binary. The initializer
+ * forces this into .data and avoids this issue. This also has the nice side-
+ * effect of the content being valid after relocation.
+ */
+static struct {
+ u64 start;
+ u64 size;
+} ram_banks[2] = {{1}};
+
+int dram_init(void)
+{
+ unsigned int na, ns;
+ const void *nvtboot_blob = (void *)nvtboot_boot_x0;
+ int node, len, i;
+ const u32 *prop;
+
+ memset(ram_banks, 0, sizeof(ram_banks));
+
+ na = fdtdec_get_uint(nvtboot_blob, 0, "#address-cells", 2);
+ ns = fdtdec_get_uint(nvtboot_blob, 0, "#size-cells", 2);
+
+ node = fdt_path_offset(nvtboot_blob, "/memory");
+ if (node < 0) {
+ error("Can't find /memory node in nvtboot DTB");
+ hang();
+ }
+ prop = fdt_getprop(nvtboot_blob, node, "reg", &len);
+ if (!prop) {
+ error("Can't find /memory/reg property in nvtboot DTB");
+ hang();
+ }
+
+ len /= (na + ns);
+ if (len > ARRAY_SIZE(ram_banks))
+ len = ARRAY_SIZE(ram_banks);
+
+ gd->ram_size = 0;
+ for (i = 0; i < len; i++) {
+ ram_banks[i].start = of_read_number(prop, na);
+ prop += na;
+ ram_banks[i].size = of_read_number(prop, ns);
+ prop += ns;
+ gd->ram_size += ram_banks[i].size;
+ }
+
+ return 0;
+}
+
+extern unsigned long nvtboot_boot_x0;
+
+void dram_init_banksize(void)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ gd->bd->bi_dram[i].start = ram_banks[i].start;
+ gd->bd->bi_dram[i].size = ram_banks[i].size;
+ }
+}
+
+ulong board_get_usable_ram_top(ulong total_size)
+{
+ return ram_banks[0].start + ram_banks[0].size;
+}
diff --git a/arch/arm/mach-tegra/tegra20/Makefile b/arch/arm/mach-tegra/tegra20/Makefile
index 17c19900e2..72d82a5218 100644
--- a/arch/arm/mach-tegra/tegra20/Makefile
+++ b/arch/arm/mach-tegra/tegra20/Makefile
@@ -10,7 +10,8 @@ endif
# The AVP is ARMv4T architecture so we must use special compiler
# flags for any startup files it might use.
-CFLAGS_warmboot_avp.o += -march=armv4t
+CFLAGS_warmboot_avp.o = -march=armv4t -U__LINUX_ARM_ARCH__ \
+ -D__LINUX_ARM_ARCH__=4
obj-y += clock.o funcmux.o pinmux.o
obj-$(CONFIG_TEGRA_LP0) += warmboot.o crypto.o warmboot_avp.o