]> git.karo-electronics.de Git - karo-tx-uboot.git/commitdiff
Merge branch 'master' of git://git.denx.de/u-boot-tegra
authorTom Rini <trini@konsulko.com>
Thu, 21 Jul 2016 22:54:58 +0000 (18:54 -0400)
committerTom Rini <trini@konsulko.com>
Thu, 21 Jul 2016 22:54:58 +0000 (18:54 -0400)
18 files changed:
arch/arm/cpu/armv8/start.S
arch/arm/dts/Makefile
arch/arm/dts/tegra186-p2771-0000-a02.dts [new file with mode: 0644]
arch/arm/dts/tegra186-p2771-0000-b00.dts [new file with mode: 0644]
arch/arm/dts/tegra186-p2771-0000.dtsi [moved from arch/arm/dts/tegra186-p2771-0000.dts with 96% similarity]
arch/arm/dts/tegra186.dtsi
arch/arm/include/asm/arch-tegra/ivc.h [new file with mode: 0644]
arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/board186.c
arch/arm/mach-tegra/ivc.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra186/Makefile
arch/arm/mach-tegra/tegra186/nvtboot_ll.S [new file with mode: 0644]
arch/arm/mach-tegra/tegra186/nvtboot_mem.c [new file with mode: 0644]
board/nvidia/p2371-2180/p2371-2180.c
board/nvidia/p2571/max77620_init.h
configs/p2771-0000-a02_defconfig [moved from configs/p2771-0000_defconfig with 85% similarity]
configs/p2771-0000-b00_defconfig [new file with mode: 0644]

index 67edf94520c5fb2214eadaf036ae38fb1942cab8..19c771dba3abc2dad0971474d2f9d7773119f771 100644 (file)
@@ -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)
index acecb7c3594ea6febf96ec6770d37fd8fbabfe3a..ca8712a7a74eb4785aee37121af1c985a70acc11 100644 (file)
@@ -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 \
diff --git a/arch/arm/dts/tegra186-p2771-0000-a02.dts b/arch/arm/dts/tegra186-p2771-0000-a02.dts
new file mode 100644 (file)
index 0000000..70f4326
--- /dev/null
@@ -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";
+};
diff --git a/arch/arm/dts/tegra186-p2771-0000-b00.dts b/arch/arm/dts/tegra186-p2771-0000-b00.dts
new file mode 100644 (file)
index 0000000..2384a65
--- /dev/null
@@ -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";
+};
similarity index 96%
rename from arch/arm/dts/tegra186-p2771-0000.dts
rename to arch/arm/dts/tegra186-p2771-0000.dtsi
index 5f29ee450194466071298ab500cee85fde12809c..87f0427e80c1c85ee2479013fe160d4a07330519 100644 (file)
@@ -1,5 +1,3 @@
-/dts-v1/;
-
 #include "tegra186.dtsi"
 
 / {
index fce34fa6500e0ed84ac04a6767921f9ad30dee60..99d49254b36b0e91553e85accb5e86fc542ff6dd 100644 (file)
@@ -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>
 
diff --git a/arch/arm/include/asm/arch-tegra/ivc.h b/arch/arm/include/asm/arch-tegra/ivc.h
new file mode 100644 (file)
index 0000000..7f2287a
--- /dev/null
@@ -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
index f4affa5512bcf8c5f78a75a8e0c7b020d1674821..85ae3b7f420992c2a125fc7c981c87fb6f4cf6a0 100644 (file)
@@ -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
 
index 12ee1cd7495b48a9055e6bb6ff154df3ed1080cf..d0bf5a6e75ef2dc178bf612d85565a66b05eef0e 100644 (file)
@@ -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/
index f4b6152a79374b3f9d87090e9ff33b854f9b634c..876ccba5e599cb45fea9351eb92448d47cbd2e2b 100644 (file)
 
 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)
 {
 }
diff --git a/arch/arm/mach-tegra/ivc.c b/arch/arm/mach-tegra/ivc.c
new file mode 100644 (file)
index 0000000..cf6626f
--- /dev/null
@@ -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;
+}
index ce4610d8f809a999f5ade4d75a298b6b718de959..033d6005fb44f25252133e20c7ad32b9dc26bcf7 100644 (file)
@@ -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 (file)
index 0000000..1eab890
--- /dev/null
@@ -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 (file)
index 0000000..37dd8d4
--- /dev/null
@@ -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;
+}
index 0f587eaaa79695aafe5f9918546cce4e643521e8..dbdc1b65e6807ee380d502dc7671b257701f553d 100644 (file)
@@ -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);
+       }
 }
 
 /*
index 92c3719112a4cedb6660a1d483b6466a7853280f..39e550149aae669bf073bcc54be7bbc90ef90f8d 100644 (file)
@@ -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
similarity index 85%
rename from configs/p2771-0000_defconfig
rename to configs/p2771-0000-a02_defconfig
index 9f2c418f9fe13ea9ad69c118da06aeeddb3b9699..1fe25f58f199d6a085a461e7bae186e144ec4b55 100644 (file)
@@ -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
diff --git a/configs/p2771-0000-b00_defconfig b/configs/p2771-0000-b00_defconfig
new file mode 100644 (file)
index 0000000..552fb6c
--- /dev/null
@@ -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