mirror of
https://github.com/u-boot/u-boot.git
synced 2025-01-24 19:53:25 +08:00
Merge branch 'master' of git://git.denx.de/u-boot-tegra
This commit is contained in:
commit
9f84da8de1
@ -53,6 +53,11 @@ _bss_end_ofs:
|
||||
.quad __bss_end - _start
|
||||
|
||||
reset:
|
||||
/* Allow the board to save important registers */
|
||||
b save_boot_params
|
||||
.globl save_boot_params_ret
|
||||
save_boot_params_ret:
|
||||
|
||||
#ifdef CONFIG_SYS_RESET_SCTRL
|
||||
bl reset_sctrl
|
||||
#endif
|
||||
@ -282,3 +287,7 @@ ENTRY(c_runtime_cpu_setup)
|
||||
|
||||
ret
|
||||
ENDPROC(c_runtime_cpu_setup)
|
||||
|
||||
WEAK(save_boot_params)
|
||||
b save_boot_params_ret /* back to my caller */
|
||||
ENDPROC(save_boot_params)
|
||||
|
@ -53,7 +53,8 @@ dtb-$(CONFIG_TEGRA) += tegra20-harmony.dtb \
|
||||
tegra124-jetson-tk1.dtb \
|
||||
tegra124-nyan-big.dtb \
|
||||
tegra124-venice2.dtb \
|
||||
tegra186-p2771-0000.dtb \
|
||||
tegra186-p2771-0000-a02.dtb \
|
||||
tegra186-p2771-0000-b00.dtb \
|
||||
tegra210-e2220-1170.dtb \
|
||||
tegra210-p2371-0000.dtb \
|
||||
tegra210-p2371-2180.dtb \
|
||||
|
8
arch/arm/dts/tegra186-p2771-0000-a02.dts
Normal file
8
arch/arm/dts/tegra186-p2771-0000-a02.dts
Normal file
@ -0,0 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include "tegra186-p2771-0000.dtsi"
|
||||
|
||||
/ {
|
||||
model = "NVIDIA P2771-0000 A02";
|
||||
compatible = "nvidia,p2771-0000-a02", "nvidia,p2771-0000", "nvidia,tegra186";
|
||||
};
|
8
arch/arm/dts/tegra186-p2771-0000-b00.dts
Normal file
8
arch/arm/dts/tegra186-p2771-0000-b00.dts
Normal file
@ -0,0 +1,8 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include "tegra186-p2771-0000.dtsi"
|
||||
|
||||
/ {
|
||||
model = "NVIDIA P2771-0000 B00";
|
||||
compatible = "nvidia,p2771-0000-b00", "nvidia,p2771-0000", "nvidia,tegra186";
|
||||
};
|
@ -1,5 +1,3 @@
|
||||
/dts-v1/;
|
||||
|
||||
#include "tegra186.dtsi"
|
||||
|
||||
/ {
|
@ -1,5 +1,5 @@
|
||||
#include "skeleton.dtsi"
|
||||
#include <dt-bindings/gpio/tegra-gpio.h>
|
||||
#include <dt-bindings/gpio/tegra186-gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/mailbox/tegra-hsp.h>
|
||||
|
||||
|
179
arch/arm/include/asm/arch-tegra/ivc.h
Normal file
179
arch/arm/include/asm/arch-tegra/ivc.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#ifndef _ASM_ARCH_TEGRA_IVC_H
|
||||
#define _ASM_ARCH_TEGRA_IVC_H
|
||||
|
||||
#include <common.h>
|
||||
|
||||
/*
|
||||
* Tegra IVC is a communication protocol that transfers fixed-size frames
|
||||
* bi-directionally and in-order between the local CPU and some remote entity.
|
||||
* Communication is via a statically sized and allocated buffer in shared
|
||||
* memory and a notification mechanism.
|
||||
*
|
||||
* This API handles all aspects of the shared memory buffer's metadata, and
|
||||
* leaves all aspects of the frame content to the calling code; frames
|
||||
* typically contain some higher-level protocol. The notification mechanism is
|
||||
* also handled externally to this API, since it can vary from instance to
|
||||
* instance.
|
||||
*
|
||||
* The client model is to first find some free (for TX) or filled (for RX)
|
||||
* frame, process that frame's memory buffer (fill or read it), and then
|
||||
* inform the protocol that the frame has been filled/read, i.e. advance the
|
||||
* write/read pointer. If the channel is full, there may be no available frames
|
||||
* to fill/read. In this case, client code may either poll for an available
|
||||
* frame, or wait for the remote entity to send a notification to the local
|
||||
* CPU.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct tegra_ivc - In-memory shared memory layout.
|
||||
*
|
||||
* This is described in detail in ivc.c.
|
||||
*/
|
||||
struct tegra_ivc_channel_header;
|
||||
|
||||
/**
|
||||
* struct tegra_ivc - Software state of an IVC channel.
|
||||
*
|
||||
* This state is internal to the IVC code and should not be accessed directly
|
||||
* by clients. It is public solely so clients can allocate storage for the
|
||||
* structure.
|
||||
*/
|
||||
struct tegra_ivc {
|
||||
/**
|
||||
* rx_channel - Pointer to the shared memory region used to receive
|
||||
* messages from the remote entity.
|
||||
*/
|
||||
struct tegra_ivc_channel_header *rx_channel;
|
||||
/**
|
||||
* tx_channel - Pointer to the shared memory region used to send
|
||||
* messages to the remote entity.
|
||||
*/
|
||||
struct tegra_ivc_channel_header *tx_channel;
|
||||
/**
|
||||
* r_pos - The position in list of frames in rx_channel that we are
|
||||
* reading from.
|
||||
*/
|
||||
uint32_t r_pos;
|
||||
/**
|
||||
* w_pos - The position in list of frames in tx_channel that we are
|
||||
* writing to.
|
||||
*/
|
||||
uint32_t w_pos;
|
||||
/**
|
||||
* nframes - The number of frames allocated (in each direction) in
|
||||
* shared memory.
|
||||
*/
|
||||
uint32_t nframes;
|
||||
/**
|
||||
* frame_size - The size of each frame in shared memory.
|
||||
*/
|
||||
uint32_t frame_size;
|
||||
/**
|
||||
* notify - Function to call to notify the remote processor of a
|
||||
* change in channel state.
|
||||
*/
|
||||
void (*notify)(struct tegra_ivc *);
|
||||
};
|
||||
|
||||
/**
|
||||
* tegra_ivc_read_get_next_frame - Locate the next frame to receive.
|
||||
*
|
||||
* Locate the next frame to be received/processed, return the address of the
|
||||
* frame, and do not remove it from the queue. Repeated calls to this function
|
||||
* will return the same address until tegra_ivc_read_advance() is called.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
* @frame Pointer to be filled with the address of the frame to receive.
|
||||
*
|
||||
* @return 0 if a frame is available, else a negative error code.
|
||||
*/
|
||||
int tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc, void **frame);
|
||||
|
||||
/**
|
||||
* tegra_ivc_read_advance - Advance the read queue.
|
||||
*
|
||||
* Inform the protocol and remote entity that the frame returned by
|
||||
* tegra_ivc_read_get_next_frame() has been processed. The remote end may then
|
||||
* re-use it to transmit further data. Subsequent to this function returning,
|
||||
* tegra_ivc_read_get_next_frame() will return a different frame.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
*
|
||||
* @return 0 if OK, else a negative error code.
|
||||
*/
|
||||
int tegra_ivc_read_advance(struct tegra_ivc *ivc);
|
||||
|
||||
/**
|
||||
* tegra_ivc_write_get_next_frame - Locate the next frame to fill for transmit.
|
||||
*
|
||||
* Locate the next frame to be filled for transmit, return the address of the
|
||||
* frame, and do not add it to the queue. Repeated calls to this function
|
||||
* will return the same address until tegra_ivc_read_advance() is called.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
* @frame Pointer to be filled with the address of the frame to fill.
|
||||
*
|
||||
* @return 0 if a frame is available, else a negative error code.
|
||||
*/
|
||||
int tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc, void **frame);
|
||||
|
||||
/**
|
||||
* tegra_ivc_write_advance - Advance the write queue.
|
||||
*
|
||||
* Inform the protocol and remote entity that the frame returned by
|
||||
* tegra_ivc_write_get_next_frame() has been filled and should be transmitted.
|
||||
* The remote end may then read data from it. Subsequent to this function
|
||||
* returning, tegra_ivc_write_get_next_frame() will return a different frame.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
*
|
||||
* @return 0 if OK, else a negative error code.
|
||||
*/
|
||||
int tegra_ivc_write_advance(struct tegra_ivc *ivc);
|
||||
|
||||
/**
|
||||
* tegra_ivc_channel_notified - handle internal messages
|
||||
*
|
||||
* This function must be called following every notification.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
*
|
||||
* @return 0 if the channel is ready for communication, or -EAGAIN if a
|
||||
* channel reset is in progress.
|
||||
*/
|
||||
int tegra_ivc_channel_notified(struct tegra_ivc *ivc);
|
||||
|
||||
/**
|
||||
* tegra_ivc_channel_reset - initiates a reset of the shared memory state
|
||||
*
|
||||
* This function must be called after a channel is initialized but before it
|
||||
* is used for communication. The channel will be ready for use when a
|
||||
* subsequent call to notify the remote of the channel reset indicates the
|
||||
* reset operation is complete.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
*/
|
||||
void tegra_ivc_channel_reset(struct tegra_ivc *ivc);
|
||||
|
||||
/**
|
||||
* tegra_ivc_init - Initialize a channel's software state.
|
||||
*
|
||||
* @ivc The IVC channel.
|
||||
* @rx_base Address of the the RX shared memory buffer.
|
||||
* @tx_base Address of the the TX shared memory buffer.
|
||||
* @nframes Number of frames in each shared memory buffer.
|
||||
* @frame_size Size of each frame.
|
||||
*
|
||||
* @return 0 if OK, else a negative error code.
|
||||
*/
|
||||
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 *));
|
||||
|
||||
#endif
|
@ -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
|
||||
@ -60,6 +68,7 @@ config TEGRA186
|
||||
select TEGRA186_GPIO
|
||||
select TEGRA_ARMV8_COMMON
|
||||
select TEGRA_HSP
|
||||
select TEGRA_IVC
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -15,23 +15,24 @@ 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-$(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/
|
||||
|
@ -11,12 +11,6 @@
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int dram_init(void)
|
||||
{
|
||||
gd->ram_size = (1.5 * 1024 * 1024 * 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_early_init_f(void)
|
||||
{
|
||||
return 0;
|
||||
@ -32,12 +26,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)
|
||||
{
|
||||
}
|
||||
|
553
arch/arm/mach-tegra/ivc.c
Normal file
553
arch/arm/mach-tegra/ivc.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
20
arch/arm/mach-tegra/tegra186/nvtboot_ll.S
Normal file
20
arch/arm/mach-tegra/tegra186/nvtboot_ll.S
Normal file
@ -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)
|
88
arch/arm/mach-tegra/tegra186/nvtboot_mem.c
Normal file
88
arch/arm/mach-tegra/tegra186/nvtboot_mem.c
Normal file
@ -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;
|
||||
}
|
@ -30,6 +30,28 @@ void pin_mux_mmc(void)
|
||||
ret = dm_i2c_write(dev, MAX77620_CNFG1_L2_REG, &val, 1);
|
||||
if (ret)
|
||||
printf("i2c_write 0 0x3c 0x27 failed: %d\n", ret);
|
||||
|
||||
/* Disable LDO4 discharge */
|
||||
ret = dm_i2c_read(dev, MAX77620_CNFG2_L4_REG, &val, 1);
|
||||
if (ret) {
|
||||
printf("i2c_read 0 0x3c 0x2c failed: %d\n", ret);
|
||||
} else {
|
||||
val &= ~BIT(1); /* ADE */
|
||||
ret = dm_i2c_write(dev, MAX77620_CNFG2_L4_REG, &val, 1);
|
||||
if (ret)
|
||||
printf("i2c_write 0 0x3c 0x2c failed: %d\n", ret);
|
||||
}
|
||||
|
||||
/* Set MBLPD */
|
||||
ret = dm_i2c_read(dev, MAX77620_CNFGGLBL1_REG, &val, 1);
|
||||
if (ret) {
|
||||
printf("i2c_write 0 0x3c 0x00 failed: %d\n", ret);
|
||||
} else {
|
||||
val |= BIT(6); /* MBLPD */
|
||||
ret = dm_i2c_write(dev, MAX77620_CNFGGLBL1_REG, &val, 1);
|
||||
if (ret)
|
||||
printf("i2c_write 0 0x3c 0x00 failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -13,6 +13,8 @@
|
||||
#define MAX77620_I2C_ADDR 0x78
|
||||
#define MAX77620_I2C_ADDR_7BIT 0x3C
|
||||
|
||||
#define MAX77620_CNFGGLBL1_REG 0x00
|
||||
|
||||
#define MAX77620_SD0_REG 0x16
|
||||
#define MAX77620_SD1_REG 0x17
|
||||
#define MAX77620_SD2_REG 0x18
|
||||
|
@ -2,10 +2,10 @@ CONFIG_ARM=y
|
||||
CONFIG_TEGRA=y
|
||||
CONFIG_TEGRA186=y
|
||||
CONFIG_TARGET_P2771_0000=y
|
||||
CONFIG_DEFAULT_DEVICE_TREE="tegra186-p2771-0000"
|
||||
CONFIG_DEFAULT_DEVICE_TREE="tegra186-p2771-0000-a02"
|
||||
CONFIG_OF_SYSTEM_SETUP=y
|
||||
CONFIG_HUSH_PARSER=y
|
||||
CONFIG_SYS_PROMPT="Tegra186 (P2771-0000) # "
|
||||
CONFIG_SYS_PROMPT="Tegra186 (P2771-0000 A02) # "
|
||||
# CONFIG_CMD_IMI is not set
|
||||
# CONFIG_CMD_IMLS is not set
|
||||
# CONFIG_CMD_FLASH is not set
|
31
configs/p2771-0000-b00_defconfig
Normal file
31
configs/p2771-0000-b00_defconfig
Normal file
@ -0,0 +1,31 @@
|
||||
CONFIG_ARM=y
|
||||
CONFIG_TEGRA=y
|
||||
CONFIG_TEGRA186=y
|
||||
CONFIG_TARGET_P2771_0000=y
|
||||
CONFIG_DEFAULT_DEVICE_TREE="tegra186-p2771-0000-b00"
|
||||
CONFIG_OF_SYSTEM_SETUP=y
|
||||
CONFIG_HUSH_PARSER=y
|
||||
CONFIG_SYS_PROMPT="Tegra186 (P2771-0000 B00) # "
|
||||
# CONFIG_CMD_IMI is not set
|
||||
# CONFIG_CMD_IMLS is not set
|
||||
# CONFIG_CMD_FLASH is not set
|
||||
CONFIG_CMD_MMC=y
|
||||
CONFIG_CMD_SF=y
|
||||
CONFIG_CMD_SPI=y
|
||||
CONFIG_CMD_I2C=y
|
||||
CONFIG_CMD_USB=y
|
||||
# CONFIG_CMD_FPGA is not set
|
||||
CONFIG_CMD_GPIO=y
|
||||
# CONFIG_CMD_SETEXPR is not set
|
||||
CONFIG_CMD_DHCP=y
|
||||
# CONFIG_CMD_NFS is not set
|
||||
CONFIG_CMD_MII=y
|
||||
CONFIG_CMD_PING=y
|
||||
CONFIG_CMD_EXT2=y
|
||||
CONFIG_CMD_EXT4=y
|
||||
CONFIG_CMD_EXT4_WRITE=y
|
||||
CONFIG_CMD_FAT=y
|
||||
CONFIG_CMD_FS_GENERIC=y
|
||||
CONFIG_SYS_NS16550=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_DM_USB=y
|
Loading…
Reference in New Issue
Block a user