diff options
39 files changed, 2838 insertions, 54 deletions
diff --git a/arch/arm/dts/tegra186-p2771-0000-a02.dts b/arch/arm/dts/tegra186-p2771-0000-a02.dts index 70f4326c09..36a3c21a55 100644 --- a/arch/arm/dts/tegra186-p2771-0000-a02.dts +++ b/arch/arm/dts/tegra186-p2771-0000-a02.dts @@ -5,4 +5,28 @@ / { model = "NVIDIA P2771-0000 A02"; compatible = "nvidia,p2771-0000-a02", "nvidia,p2771-0000", "nvidia,tegra186"; + + sdhci@3400000 { + cd-gpios = <&gpio_main TEGRA_MAIN_GPIO(P, 6) GPIO_ACTIVE_LOW>; + power-gpios = <&gpio_main TEGRA_MAIN_GPIO(P, 5) GPIO_ACTIVE_HIGH>; + }; + + pcie-controller@10003000 { + status = "okay"; + + pci@1,0 { + status = "okay"; + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + status = "disabled"; + nvidia,num-lanes = <1>; + }; + + pci@3,0 { + status = "okay"; + nvidia,num-lanes = <1>; + }; + }; }; diff --git a/arch/arm/dts/tegra186-p2771-0000-b00.dts b/arch/arm/dts/tegra186-p2771-0000-b00.dts index 2384a65e87..0f1d0e83e8 100644 --- a/arch/arm/dts/tegra186-p2771-0000-b00.dts +++ b/arch/arm/dts/tegra186-p2771-0000-b00.dts @@ -5,4 +5,28 @@ / { model = "NVIDIA P2771-0000 B00"; compatible = "nvidia,p2771-0000-b00", "nvidia,p2771-0000", "nvidia,tegra186"; + + sdhci@3400000 { + cd-gpios = <&gpio_main TEGRA_MAIN_GPIO(P, 5) GPIO_ACTIVE_LOW>; + power-gpios = <&gpio_main TEGRA_MAIN_GPIO(P, 6) GPIO_ACTIVE_HIGH>; + }; + + pcie-controller@10003000 { + status = "okay"; + + pci@1,0 { + status = "okay"; + nvidia,num-lanes = <4>; + }; + + pci@2,0 { + status = "disabled"; + nvidia,num-lanes = <0>; + }; + + pci@3,0 { + status = "disabled"; + nvidia,num-lanes = <1>; + }; + }; }; diff --git a/arch/arm/dts/tegra186-p2771-0000.dtsi b/arch/arm/dts/tegra186-p2771-0000.dtsi index 87f0427e80..d867674fd0 100644 --- a/arch/arm/dts/tegra186-p2771-0000.dtsi +++ b/arch/arm/dts/tegra186-p2771-0000.dtsi @@ -10,14 +10,63 @@ aliases { sdhci0 = "/sdhci@3460000"; + sdhci1 = "/sdhci@3400000"; + i2c0 = "/bpmp/i2c"; + i2c1 = "/i2c@3160000"; + i2c2 = "/i2c@c240000"; + i2c3 = "/i2c@3180000"; + i2c4 = "/i2c@3190000"; + i2c5 = "/i2c@31c0000"; + i2c6 = "/i2c@c250000"; + i2c7 = "/i2c@31e0000"; }; memory { reg = <0x0 0x80000000 0x0 0x60000000>; }; + i2c@3160000 { + status = "okay"; + }; + + i2c@3180000 { + status = "okay"; + }; + + i2c@3190000 { + status = "okay"; + }; + + i2c@31c0000 { + status = "okay"; + }; + + sdhci@3400000 { + status = "okay"; + wp-gpios = <&gpio_main TEGRA_MAIN_GPIO(P, 4) GPIO_ACTIVE_HIGH>; + bus-width = <4>; + }; + sdhci@3460000 { status = "okay"; bus-width = <8>; }; + + i2c@c240000 { + status = "okay"; + }; + + i2c@c250000 { + status = "okay"; + }; + + i2c@31e0000 { + status = "okay"; + }; + + bpmp { + i2c { + status = "okay"; + }; + }; }; diff --git a/arch/arm/include/asm/arch-tegra/bpmp_abi.h b/arch/arm/include/asm/arch-tegra/bpmp_abi.h new file mode 100644 index 0000000000..7b6ad899cf --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/bpmp_abi.h @@ -0,0 +1,1591 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _ABI_BPMP_ABI_H_ +#define _ABI_BPMP_ABI_H_ + +#ifdef LK +#include <stdint.h> +#endif + +#ifndef __ABI_PACKED +#define __ABI_PACKED __attribute__((packed)) +#endif + +#ifdef NO_GCC_EXTENSIONS +#define EMPTY char empty; +#define EMPTY_ARRAY 1 +#else +#define EMPTY +#define EMPTY_ARRAY 0 +#endif + +#ifndef __UNION_ANON +#define __UNION_ANON +#endif +/** + * @file + */ + + +/** + * @defgroup MRQ MRQ Messages + * @brief Messages sent to/from BPMP via IPC + * @{ + * @defgroup MRQ_Format Message Format + * @defgroup MRQ_Codes Message Request (MRQ) Codes + * @defgroup MRQ_Payloads Message Payloads + * @defgroup Error_Codes Error Codes + * @} + */ + +/** + * @addtogroup MRQ_Format Message Format + * @{ + * The CPU requests the BPMP to perform a particular service by + * sending it an IVC frame containing a single MRQ message. An MRQ + * message consists of a @ref mrq_request followed by a payload whose + * format depends on mrq_request::mrq. + * + * The BPMP processes the data and replies with an IVC frame (on the + * same IVC channel) containing and MRQ response. An MRQ response + * consists of a @ref mrq_response followed by a payload whose format + * depends on the associated mrq_request::mrq. + * + * A well-defined subset of the MRQ messages that the CPU sends to the + * BPMP can lead to BPMP eventually sending an MRQ message to the + * CPU. For example, when the CPU uses an #MRQ_THERMAL message to set + * a thermal trip point, the BPMP may eventually send a single + * #MRQ_THERMAL message of its own to the CPU indicating that the trip + * point has been crossed. + * @} + */ + +/** + * @ingroup MRQ_Format + * @brief header for an MRQ message + * + * Provides the MRQ number for the MRQ message: #mrq. The remainder of + * the MRQ message is a payload (immediately following the + * mrq_request) whose format depends on mrq. + * + * @todo document the flags + */ +struct mrq_request { + /** @brief MRQ number of the request */ + uint32_t mrq; + /** @brief flags for the request */ + uint32_t flags; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Format + * @brief header for an MRQ response + * + * Provides an error code for the associated MRQ message. The + * remainder of the MRQ response is a payload (immediately following + * the mrq_response) whose format depends on the associated + * mrq_request::mrq + * + * @todo document the flags + */ +struct mrq_response { + /** @brief error code for the MRQ request itself */ + int32_t err; + /** @brief flags for the response */ + uint32_t flags; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Format + * Minimum needed size for an IPC message buffer + */ +#define MSG_MIN_SZ 128 +/** + * @ingroup MRQ_Format + * Minimum size guaranteed for data in an IPC message buffer + */ +#define MSG_DATA_MIN_SZ 120 + +/** + * @ingroup MRQ_Codes + * @name Legal MRQ codes + * These are the legal values for mrq_request::mrq + * @{ + */ + +#define MRQ_PING 0 +#define MRQ_QUERY_TAG 1 +#define MRQ_MODULE_LOAD 4 +#define MRQ_MODULE_UNLOAD 5 +#define MRQ_TRACE_MODIFY 7 +#define MRQ_WRITE_TRACE 8 +#define MRQ_THREADED_PING 9 +#define MRQ_MODULE_MAIL 11 +#define MRQ_DEBUGFS 19 +#define MRQ_RESET 20 +#define MRQ_I2C 21 +#define MRQ_CLK 22 +#define MRQ_QUERY_ABI 23 +#define MRQ_PG_READ_STATE 25 +#define MRQ_PG_UPDATE_STATE 26 +#define MRQ_THERMAL 27 +#define MRQ_CPU_VHINT 28 +#define MRQ_ABI_RATCHET 29 +#define MRQ_EMC_DVFS_LATENCY 31 +#define MRQ_TRACE_ITER 64 + +/** @} */ + +/** + * @ingroup MRQ_Codes + * @brief Maximum MRQ code to be sent by CPU software to + * BPMP. Subject to change in future + */ +#define MAX_CPU_MRQ_ID 64 + +/** + * @addtogroup MRQ_Payloads Message Payloads + * @{ + * @defgroup Ping + * @defgroup Query_Tag Query Tag + * @defgroup Module Loadable Modules + * @defgroup Trace + * @defgroup Debugfs + * @defgroup Reset + * @defgroup I2C + * @defgroup Clocks + * @defgroup ABI_info ABI Info + * @defgroup MC_Flush MC Flush + * @defgroup Powergating + * @defgroup Thermal + * @defgroup Vhint CPU Voltage hint + * @defgroup MRQ_Deprecated Deprecated MRQ messages + * @defgroup EMC + * @} + */ + + +/** + * @ingroup MRQ_Codes + * @def MRQ_PING + * @brief A simple ping + * + * * Platforms: All + * * Initiators: Any + * * Targets: Any + * * Request Payload: @ref mrq_ping_request + * * Response Payload: @ref mrq_ping_response + * + * @ingroup MRQ_Codes + * @def MRQ_THREADED_PING + * @brief A deeper ping + * + * * Platforms: All + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_ping_request + * * Response Payload: @ref mrq_ping_response + * + * Behavior is equivalent to a simple #MRQ_PING except that BPMP + * responds from a thread context (providing a slightly more robust + * sign of life). + * + */ + +/** + * @ingroup Ping + * @brief request with #MRQ_PING + * + * Used by the sender of an #MRQ_PING message to request a pong from + * recipient. The response from the recipient is computed based on + * #challenge. + */ +struct mrq_ping_request { +/** @brief arbitrarily chosen value */ + uint32_t challenge; +} __ABI_PACKED; + +/** + * @ingroup Ping + * @brief response to #MRQ_PING + * + * Sent in response to an #MRQ_PING message. #reply should be the + * mrq_ping_request challenge left shifted by 1 with the carry-bit + * dropped. + * + */ +struct mrq_ping_response { + /** @brief response to the MRQ_PING challege */ + uint32_t reply; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_QUERY_TAG + * @brief Query BPMP firmware's tag (i.e. version information) + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_query_tag_request + * * Response Payload: N/A + * + */ + +/** + * @ingroup Query_Tag + * @brief request with #MRQ_QUERY_TAG + * + * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory + * pointed by #addr with BPMP firmware header. + * + * The sender is reponsible for ensuring that #addr is mapped in to + * the recipient's address map. + */ +struct mrq_query_tag_request { + /** @brief base address to store the firmware header */ + uint32_t addr; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_MODULE_LOAD + * @brief dynamically load a BPMP code module + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_module_load_request + * * Response Payload: @ref mrq_module_load_response + * + * @note This MRQ is disabled on production systems + * + */ + +/** + * @ingroup Module + * @brief request with #MRQ_MODULE_LOAD + * + * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically + * load the code located at #phys_addr and having size #size + * bytes. #phys_addr is treated as a void pointer. + * + * The recipient copies the code from #phys_addr to locally allocated + * memory prior to responding to this message. + * + * @todo document the module header format + * + * The sender is responsible for ensuring that the code is mapped in + * the recipient's address map. + * + */ +struct mrq_module_load_request { + /** @brief base address of the code to load. Treated as (void *) */ + uint32_t phys_addr; /* (void *) */ + /** @brief size in bytes of code to load */ + uint32_t size; +} __ABI_PACKED; + +/** + * @ingroup Module + * @brief response to #MRQ_MODULE_LOAD + * + * @todo document mrq_response::err + */ +struct mrq_module_load_response { + /** @brief handle to the loaded module */ + uint32_t base; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_MODULE_UNLOAD + * @brief unload a previously loaded code module + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_module_unload_request + * * Response Payload: N/A + * + * @note This MRQ is disabled on production systems + */ + +/** + * @ingroup Module + * @brief request with #MRQ_MODULE_UNLOAD + * + * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded + * module be unloaded. + */ +struct mrq_module_unload_request { + /** @brief handle of the module to unload */ + uint32_t base; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_TRACE_MODIFY + * @brief modify the set of enabled trace events + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_trace_modify_request + * * Response Payload: @ref mrq_trace_modify_response + * + * @note This MRQ is disabled on production systems + */ + +/** + * @ingroup Trace + * @brief request with #MRQ_TRACE_MODIFY + * + * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace + * events. #set takes precedence for any bit set in both #set and + * #clr. + */ +struct mrq_trace_modify_request { + /** @brief bit mask of trace events to disable */ + uint32_t clr; + /** @brief bit mask of trace events to enable */ + uint32_t set; +} __ABI_PACKED; + +/** + * @ingroup Trace + * @brief response to #MRQ_TRACE_MODIFY + * + * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the + * state of which events are enabled after the recipient acted on the + * message. + * + */ +struct mrq_trace_modify_response { + /** @brief bit mask of trace event enable states */ + uint32_t mask; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_WRITE_TRACE + * @brief Write trace data to a buffer + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_write_trace_request + * * Response Payload: @ref mrq_write_trace_response + * + * mrq_response::err depends on the @ref mrq_write_trace_request field + * values. err is -#BPMP_EINVAL if size is zero or area is NULL or + * area is in an illegal range. A positive value for err indicates the + * number of bytes written to area. + * + * @note This MRQ is disabled on production systems + */ + +/** + * @ingroup Trace + * @brief request with #MRQ_WRITE_TRACE + * + * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace + * data from the recipient's local buffer to the output buffer. #area + * is treated as a byte-aligned pointer in the recipient's address + * space. + * + * The sender is responsible for ensuring that the output + * buffer is mapped in the recipient's address map. The recipient is + * responsible for protecting its own code and data from accidental + * overwrites. + */ +struct mrq_write_trace_request { + /** @brief base address of output buffer */ + uint32_t area; + /** @brief size in bytes of the output buffer */ + uint32_t size; +} __ABI_PACKED; + +/** + * @ingroup Trace + * @brief response to #MRQ_WRITE_TRACE + * + * Once this response is sent, the respondent will not access the + * output buffer further. + */ +struct mrq_write_trace_response { + /** + * @brief flag whether more data remains in local buffer + * + * Value is 1 if the entire local trace buffer has been + * drained to the outputbuffer. Value is 0 otherwise. + */ + uint32_t eof; +} __ABI_PACKED; + +/** @private */ +struct mrq_threaded_ping_request { + uint32_t challenge; +} __ABI_PACKED; + +/** @private */ +struct mrq_threaded_ping_response { + uint32_t reply; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_MODULE_MAIL + * @brief send a message to a loadable module + * + * * Platforms: All + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_module_mail_request + * * Response Payload: @ref mrq_module_mail_response + * + * @note This MRQ is disabled on production systems + */ + +/** + * @ingroup Module + * @brief request with #MRQ_MODULE_MAIL + */ +struct mrq_module_mail_request { + /** @brief handle to the previously loaded module */ + uint32_t base; + /** @brief module-specific mail payload + * + * The length of data[ ] is unknown to the BPMP core firmware + * but it is limited to the size of an IPC message. + */ + uint8_t data[EMPTY_ARRAY]; +} __ABI_PACKED; + +/** + * @ingroup Module + * @brief response to #MRQ_MODULE_MAIL + */ +struct mrq_module_mail_response { + /** @brief module-specific mail payload + * + * The length of data[ ] is unknown to the BPMP core firmware + * but it is limited to the size of an IPC message. + */ + uint8_t data[EMPTY_ARRAY]; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_DEBUGFS + * @brief Interact with BPMP's debugfs file nodes + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_debugfs_request + * * Response Payload: @ref mrq_debugfs_response + */ + +/** + * @addtogroup Debugfs + * @{ + * + * The BPMP firmware implements a pseudo-filesystem called + * debugfs. Any driver within the firmware may register with debugfs + * to expose an arbitrary set of "files" in the filesystem. When + * software on the CPU writes to a debugfs file, debugfs passes the + * written data to a callback provided by the driver. When software on + * the CPU reads a debugfs file, debugfs queries the driver for the + * data to return to the CPU. The intention of the debugfs filesystem + * is to provide information useful for debugging the system at + * runtime. + * + * @note The files exposed via debugfs are not part of the + * BPMP firmware's ABI. debugfs files may be added or removed in any + * given version of the firmware. Typically the semantics of a debugfs + * file are consistent from version to version but even that is not + * guaranteed. + * + * @} + */ +/** @ingroup Debugfs */ +enum mrq_debugfs_commands { + CMD_DEBUGFS_READ = 1, + CMD_DEBUGFS_WRITE = 2, + CMD_DEBUGFS_DUMPDIR = 3, + CMD_DEBUGFS_MAX +}; + +/** + * @ingroup Debugfs + * @brief parameters for CMD_DEBUGFS_READ/WRITE command + */ +struct cmd_debugfs_fileop_request { + /** @brief physical address pointing at filename */ + uint32_t fnameaddr; + /** @brief length in bytes of filename buffer */ + uint32_t fnamelen; + /** @brief physical address pointing to data buffer */ + uint32_t dataaddr; + /** @brief length in bytes of data buffer */ + uint32_t datalen; +} __ABI_PACKED; + +/** + * @ingroup Debugfs + * @brief parameters for CMD_DEBUGFS_READ/WRITE command + */ +struct cmd_debugfs_dumpdir_request { + /** @brief physical address pointing to data buffer */ + uint32_t dataaddr; + /** @brief length in bytes of data buffer */ + uint32_t datalen; +} __ABI_PACKED; + +/** + * @ingroup Debugfs + * @brief response data for CMD_DEBUGFS_READ/WRITE command + */ +struct cmd_debugfs_fileop_response { + /** @brief always 0 */ + uint32_t reserved; + /** @brief number of bytes read from or written to data buffer */ + uint32_t nbytes; +} __ABI_PACKED; + +/** + * @ingroup Debugfs + * @brief response data for CMD_DEBUGFS_DUMPDIR command + */ +struct cmd_debugfs_dumpdir_response { + /** @brief always 0 */ + uint32_t reserved; + /** @brief number of bytes read from or written to data buffer */ + uint32_t nbytes; +} __ABI_PACKED; + +/** + * @ingroup Debugfs + * @brief request with #MRQ_DEBUGFS. + * + * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs + * command to execute. Legal commands are the values of @ref + * mrq_debugfs_commands. Each command requires a specific additional + * payload of data. + * + * |command |payload| + * |-------------------|-------| + * |CMD_DEBUGFS_READ |fop | + * |CMD_DEBUGFS_WRITE |fop | + * |CMD_DEBUGFS_DUMPDIR|dumpdir| + */ +struct mrq_debugfs_request { + uint32_t cmd; + union { + struct cmd_debugfs_fileop_request fop; + struct cmd_debugfs_dumpdir_request dumpdir; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup Debugfs + */ +struct mrq_debugfs_response { + /** @brief always 0 */ + int32_t reserved; + union { + /** @brief response data for CMD_DEBUGFS_READ OR + * CMD_DEBUGFS_WRITE command + */ + struct cmd_debugfs_fileop_response fop; + /** @brief response data for CMD_DEBUGFS_DUMPDIR command */ + struct cmd_debugfs_dumpdir_response dumpdir; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @addtogroup Debugfs + * @{ + */ +#define DEBUGFS_S_ISDIR (1 << 9) +#define DEBUGFS_S_IRUSR (1 << 8) +#define DEBUGFS_S_IWUSR (1 << 7) +/** @} */ + + +/** + * @ingroup MRQ_Codes + * @def MRQ_RESET + * @brief reset an IP block + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_reset_request + * * Response Payload: N/A + */ + +/** + * @ingroup Reset + */ +enum mrq_reset_commands { + CMD_RESET_ASSERT = 1, + CMD_RESET_DEASSERT = 2, + CMD_RESET_MODULE = 3, + CMD_RESET_MAX, /* not part of ABI and subject to change */ +}; + +/** + * @ingroup Reset + * @brief request with MRQ_RESET + * + * Used by the sender of an #MRQ_RESET message to request BPMP to + * assert or or deassert a given reset line. + */ +struct mrq_reset_request { + /** @brief reset action to perform (@enum mrq_reset_commands) */ + uint32_t cmd; + /** @brief id of the reset to affected */ + uint32_t reset_id; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_I2C + * @brief issue an i2c transaction + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_i2c_request + * * Response Payload: @ref mrq_i2c_response + */ + +/** + * @addtogroup I2C + * @{ + */ +#define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE (MSG_DATA_MIN_SZ - 12) +#define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE (MSG_DATA_MIN_SZ - 4) +/** @} */ + +/** + * @ingroup I2C + * @name Serial I2C flags + * Use these flags with serial_i2c_request::flags + * @{ + */ +#define SERIALI2C_TEN 0x0010 +#define SERIALI2C_RD 0x0001 +#define SERIALI2C_STOP 0x8000 +#define SERIALI2C_NOSTART 0x4000 +#define SERIALI2C_REV_DIR_ADDR 0x2000 +#define SERIALI2C_IGNORE_NAK 0x1000 +#define SERIALI2C_NO_RD_ACK 0x0800 +#define SERIALI2C_RECV_LEN 0x0400 +/** @} */ +/** @ingroup I2C */ +enum { + CMD_I2C_XFER = 1 +}; + +/** + * @ingroup I2C + * @brief serializable i2c request + * + * Instances of this structure are packed (little-endian) into + * cmd_i2c_xfer_request::data_buf. Each instance represents a single + * transaction (or a portion of a transaction with repeated starts) on + * an i2c bus. + * + * Because these structures are packed, some instances are likely to + * be misaligned. Additionally because #data is variable length, it is + * not possible to iterate through a serialized list of these + * structures without inspecting #len in each instance. It may be + * easier to serialize or deserialize cmd_i2c_xfer_request::data_buf + * manually rather than using this structure definition. +*/ +struct serial_i2c_request { + /** @brief I2C slave address */ + uint16_t addr; + /** @brief bitmask of SERIALI2C_ flags */ + uint16_t flags; + /** @brief length of I2C transaction in bytes */ + uint16_t len; + /** @brief for write transactions only, #len bytes of data */ + uint8_t data[]; +} __ABI_PACKED; + +/** + * @ingroup I2C + * @brief trigger one or more i2c transactions + */ +struct cmd_i2c_xfer_request { + /** @brief valid bus number from mach-t186/i2c-t186.h*/ + uint32_t bus_id; + + /** @brief count of valid bytes in #data_buf*/ + uint32_t data_size; + + /** @brief serialized packed instances of @ref serial_i2c_request*/ + uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE]; +} __ABI_PACKED; + +/** + * @ingroup I2C + * @brief container for data read from the i2c bus + * + * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute + * zero or more I2C reads. The data read from the bus is serialized + * into #data_buf. + */ +struct cmd_i2c_xfer_response { + /** @brief count of valid bytes in #data_buf*/ + uint32_t data_size; + /** @brief i2c read data */ + uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE]; +} __ABI_PACKED; + +/** + * @ingroup I2C + * @brief request with #MRQ_I2C + */ +struct mrq_i2c_request { + /** @brief always CMD_I2C_XFER (i.e. 1) */ + uint32_t cmd; + /** @brief parameters of the transfer request */ + struct cmd_i2c_xfer_request xfer; +} __ABI_PACKED; + +/** + * @ingroup I2C + * @brief response to #MRQ_I2C + */ +struct mrq_i2c_response { + struct cmd_i2c_xfer_response xfer; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_CLK + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_clk_request + * * Response Payload: @ref mrq_clk_response + * @addtogroup Clocks + * @{ + */ + +/** + * @name MRQ_CLK sub-commands + * @{ + */ +enum { + CMD_CLK_GET_RATE = 1, + CMD_CLK_SET_RATE = 2, + CMD_CLK_ROUND_RATE = 3, + CMD_CLK_GET_PARENT = 4, + CMD_CLK_SET_PARENT = 5, + CMD_CLK_IS_ENABLED = 6, + CMD_CLK_ENABLE = 7, + CMD_CLK_DISABLE = 8, + CMD_CLK_GET_ALL_INFO = 14, + CMD_CLK_GET_MAX_CLK_ID = 15, + CMD_CLK_MAX, +}; +/** @} */ + +#define MRQ_CLK_NAME_MAXLEN 40 +#define MRQ_CLK_MAX_PARENTS 16 + +/** @private */ +struct cmd_clk_get_rate_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_get_rate_response { + int64_t rate; +} __ABI_PACKED; + +struct cmd_clk_set_rate_request { + int32_t unused; + int64_t rate; +} __ABI_PACKED; + +struct cmd_clk_set_rate_response { + int64_t rate; +} __ABI_PACKED; + +struct cmd_clk_round_rate_request { + int32_t unused; + int64_t rate; +} __ABI_PACKED; + +struct cmd_clk_round_rate_response { + int64_t rate; +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_get_parent_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_get_parent_response { + uint32_t parent_id; +} __ABI_PACKED; + +struct cmd_clk_set_parent_request { + uint32_t parent_id; +} __ABI_PACKED; + +struct cmd_clk_set_parent_response { + uint32_t parent_id; +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_is_enabled_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_is_enabled_response { + int32_t state; +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_enable_request { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_enable_response { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_disable_request { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_disable_response { + EMPTY +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_get_all_info_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_get_all_info_response { + uint32_t flags; + uint32_t parent; + uint32_t parents[MRQ_CLK_MAX_PARENTS]; + uint8_t num_parents; + uint8_t name[MRQ_CLK_NAME_MAXLEN]; +} __ABI_PACKED; + +/** @private */ +struct cmd_clk_get_max_clk_id_request { + EMPTY +} __ABI_PACKED; + +struct cmd_clk_get_max_clk_id_response { + uint32_t max_id; +} __ABI_PACKED; +/** @} */ + +/** + * @ingroup Clocks + * @brief request with #MRQ_CLK + * + * Used by the sender of an #MRQ_CLK message to control clocks. The + * clk_request is split into several sub-commands. Some sub-commands + * require no additional data. Others have a sub-command specific + * payload + * + * |sub-command |payload | + * |----------------------------|-----------------------| + * |CMD_CLK_GET_RATE |- | + * |CMD_CLK_SET_RATE |clk_set_rate | + * |CMD_CLK_ROUND_RATE |clk_round_rate | + * |CMD_CLK_GET_PARENT |- | + * |CMD_CLK_SET_PARENT |clk_set_parent | + * |CMD_CLK_IS_ENABLED |- | + * |CMD_CLK_ENABLE |- | + * |CMD_CLK_DISABLE |- | + * |CMD_CLK_GET_ALL_INFO |- | + * |CMD_CLK_GET_MAX_CLK_ID |- | + * + */ + +struct mrq_clk_request { + /** @brief sub-command and clock id concatenated to 32-bit word. + * - bits[31..24] is the sub-cmd. + * - bits[23..0] is the clock id + */ + uint32_t cmd_and_id; + + union { + /** @private */ + struct cmd_clk_get_rate_request clk_get_rate; + struct cmd_clk_set_rate_request clk_set_rate; + struct cmd_clk_round_rate_request clk_round_rate; + /** @private */ + struct cmd_clk_get_parent_request clk_get_parent; + struct cmd_clk_set_parent_request clk_set_parent; + /** @private */ + struct cmd_clk_enable_request clk_enable; + /** @private */ + struct cmd_clk_disable_request clk_disable; + /** @private */ + struct cmd_clk_is_enabled_request clk_is_enabled; + /** @private */ + struct cmd_clk_get_all_info_request clk_get_all_info; + /** @private */ + struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup Clocks + * @brief response to MRQ_CLK + * + * Each sub-command supported by @ref mrq_clk_request may return + * sub-command-specific data. Some do and some do not as indicated in + * the following table + * + * |sub-command |payload | + * |----------------------------|------------------------| + * |CMD_CLK_GET_RATE |clk_get_rate | + * |CMD_CLK_SET_RATE |clk_set_rate | + * |CMD_CLK_ROUND_RATE |clk_round_rate | + * |CMD_CLK_GET_PARENT |clk_get_parent | + * |CMD_CLK_SET_PARENT |clk_set_parent | + * |CMD_CLK_IS_ENABLED |clk_is_enabled | + * |CMD_CLK_ENABLE |- | + * |CMD_CLK_DISABLE |- | + * |CMD_CLK_GET_ALL_INFO |clk_get_all_info | + * |CMD_CLK_GET_MAX_CLK_ID |clk_get_max_id | + * + */ + +struct mrq_clk_response { + union { + struct cmd_clk_get_rate_response clk_get_rate; + struct cmd_clk_set_rate_response clk_set_rate; + struct cmd_clk_round_rate_response clk_round_rate; + struct cmd_clk_get_parent_response clk_get_parent; + struct cmd_clk_set_parent_response clk_set_parent; + /** @private */ + struct cmd_clk_enable_response clk_enable; + /** @private */ + struct cmd_clk_disable_response clk_disable; + struct cmd_clk_is_enabled_response clk_is_enabled; + struct cmd_clk_get_all_info_response clk_get_all_info; + struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id; + } __UNION_ANON; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_QUERY_ABI + * @brief check if an MRQ is implemented + * + * * Platforms: All + * * Initiators: Any + * * Targets: Any + * * Request Payload: @ref mrq_query_abi_request + * * Response Payload: @ref mrq_query_abi_response + */ + +/** + * @ingroup ABI_info + * @brief request with MRQ_QUERY_ABI + * + * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported + * by the recipient. + */ +struct mrq_query_abi_request { + /** @brief MRQ code to query */ + uint32_t mrq; +} __ABI_PACKED; + +/** + * @ingroup ABI_info + * @brief response to MRQ_QUERY_ABI + */ +struct mrq_query_abi_response { + /** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */ + int32_t status; +} __ABI_PACKED; + +/** + * @ingroup MRQ_Codes + * @def MRQ_PG_READ_STATE + * @brief read the power-gating state of a partition + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_pg_read_state_request + * * Response Payload: @ref mrq_pg_read_state_response + * @addtogroup Powergating + * @{ + */ + +/** + * @brief request with #MRQ_PG_READ_STATE + * + * Used by MRQ_PG_READ_STATE call to read the current state of a + * partition. + */ +struct mrq_pg_read_state_request { + /** @brief ID of partition */ + uint32_t partition_id; +} __ABI_PACKED; + +/** + * @brief response to MRQ_PG_READ_STATE + * @todo define possible errors. + */ +struct mrq_pg_read_state_response { + /** @brief read as don't care */ + uint32_t sram_state; + /** @brief state of power partition + * * 0 : off + * * 1 : on + */ + uint32_t logic_state; +} __ABI_PACKED; + +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_PG_UPDATE_STATE + * @brief modify the power-gating state of a partition + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_pg_update_state_request + * * Response Payload: N/A + * @addtogroup Powergating + * @{ + */ + +/** + * @brief request with mrq_pg_update_state_request + * + * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the + * state of a power partition #partition_id. + */ +struct mrq_pg_update_state_request { + /** @brief ID of partition */ + uint32_t partition_id; + /** @brief secondary control of power partition + * @details Ignored by many versions of the BPMP + * firmware. For maximum compatibility, set the value + * according to @logic_state + * * 0x1: power ON partition (@ref logic_state == 0x3) + * * 0x3: power OFF partition (@ref logic_state == 0x1) + */ + uint32_t sram_state; + /** @brief controls state of power partition, legal values are + * * 0x1 : power OFF partition + * * 0x3 : power ON partition + */ + uint32_t logic_state; + /** @brief change state of clocks of the power partition, legal values + * * 0x0 : do not change clock state + * * 0x1 : disable partition clocks (only applicable when + * @ref logic_state == 0x1) + * * 0x3 : enable partition clocks (only applicable when + * @ref logic_state == 0x3) + */ + uint32_t clock_state; +} __ABI_PACKED; +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_THERMAL + * @brief interact with BPMP thermal framework + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: Any + * * Request Payload: TODO + * * Response Payload: TODO + * + * @addtogroup Thermal + * + * The BPMP firmware includes a thermal framework. Drivers within the + * bpmp firmware register with the framework to provide thermal + * zones. Each thermal zone corresponds to an entity whose temperature + * can be measured. The framework also has a notion of trip points. A + * trip point consists of a thermal zone id, a temperature, and a + * callback routine. The framework invokes the callback when the zone + * hits the indicated temperature. The BPMP firmware uses this thermal + * framework interally to implement various temperature-dependent + * functions. + * + * Software on the CPU can use #MRQ_THERMAL (with payload @ref + * mrq_thermal_host_to_bpmp_request) to interact with the BPMP thermal + * framework. The CPU must It can query the number of supported zones, + * query zone temperatures, and set trip points. + * + * When a trip point set by the CPU gets crossed, BPMP firmware issues + * an IPC to the CPU having mrq_request::mrq = #MRQ_THERMAL and a + * payload of @ref mrq_thermal_bpmp_to_host_request. + * @{ + */ +enum mrq_thermal_host_to_bpmp_cmd { + /** + * @brief Check whether the BPMP driver supports the specified + * request type. + * + * Host needs to supply request parameters. + * + * mrq_response::err is 0 if the specified request is + * supported and -#BPMP_ENODEV otherwise. + */ + CMD_THERMAL_QUERY_ABI = 0, + + /** + * @brief Get the current temperature of the specified zone. + * + * Host needs to supply request parameters. + * + * mrq_response::err is + * * 0: Temperature query succeeded. + * * -#BPMP_EINVAL: Invalid request parameters. + * * -#BPMP_ENOENT: No driver registered for thermal zone.. + * * -#BPMP_EFAULT: Problem reading temperature measurement. + */ + CMD_THERMAL_GET_TEMP = 1, + + /** + * @brief Enable or disable and set the lower and upper + * thermal limits for a thermal trip point. Each zone has + * one trip point. + * + * Host needs to supply request parameters. Once the + * temperature hits a trip point, the BPMP will send a message + * to the CPU having MRQ=MRQ_THERMAL and + * type=CMD_THERMAL_HOST_TRIP_REACHED + * + * mrq_response::err is + * * 0: Trip successfully set. + * * -#BPMP_EINVAL: Invalid request parameters. + * * -#BPMP_ENOENT: No driver registered for thermal zone. + * * -#BPMP_EFAULT: Problem setting trip point. + */ + CMD_THERMAL_SET_TRIP = 2, + + /** + * @brief Get the number of supported thermal zones. + * + * No request parameters required. + * + * mrq_response::err is always 0, indicating success. + */ + CMD_THERMAL_GET_NUM_ZONES = 3, + + /** @brief: number of supported host-to-bpmp commands. May + * increase in future + */ + CMD_THERMAL_HOST_TO_BPMP_NUM +}; + +enum mrq_thermal_bpmp_to_host_cmd { + /** + * @brief Indication that the temperature for a zone has + * exceeded the range indicated in the thermal trip point + * for the zone. + * + * BPMP needs to supply request parameters. Host only needs to + * acknowledge. + */ + CMD_THERMAL_HOST_TRIP_REACHED = 100, + + /** @brief: number of supported bpmp-to-host commands. May + * increase in future + */ + CMD_THERMAL_BPMP_TO_HOST_NUM +}; + +/* + * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI + * + * zone: Request type for which to check existence. + */ +struct cmd_thermal_query_abi_request { + uint32_t type; +} __ABI_PACKED; + +/* + * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP + * + * zone: Number of thermal zone. + */ +struct cmd_thermal_get_temp_request { + uint32_t zone; +} __ABI_PACKED; + +/* + * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP + * + * error: 0 if request succeeded. + * -BPMP_EINVAL if request parameters were invalid. + * -BPMP_ENOENT if no driver was registered for the specified thermal zone. + * -BPMP_EFAULT for other thermal zone driver errors. + * temp: Current temperature in millicelsius. + */ +struct cmd_thermal_get_temp_response { + int32_t temp; +} __ABI_PACKED; + +/* + * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP + * + * zone: Number of thermal zone. + * low: Temperature of lower trip point in millicelsius + * high: Temperature of upper trip point in millicelsius + * enabled: 1 to enable trip point, 0 to disable trip point + */ +struct cmd_thermal_set_trip_request { + uint32_t zone; + int32_t low; + int32_t high; + uint32_t enabled; +} __ABI_PACKED; + +/* + * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED + * + * zone: Number of thermal zone where trip point was reached. + */ +struct cmd_thermal_host_trip_reached_request { + uint32_t zone; +} __ABI_PACKED; + +/* + * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES + * + * num: Number of supported thermal zones. The thermal zones are indexed + * starting from zero. + */ +struct cmd_thermal_get_num_zones_response { + uint32_t num; +} __ABI_PACKED; + +/* + * Host->BPMP request data. + * + * Reply type is union mrq_thermal_bpmp_to_host_response. + * + * type: Type of request. Values listed in enum mrq_thermal_type. + * data: Request type specific parameters. + */ +struct mrq_thermal_host_to_bpmp_request { + uint32_t type; + union { + struct cmd_thermal_query_abi_request query_abi; + struct cmd_thermal_get_temp_request get_temp; + struct cmd_thermal_set_trip_request set_trip; + } __UNION_ANON; +} __ABI_PACKED; + +/* + * BPMP->Host request data. + * + * type: Type of request. Values listed in enum mrq_thermal_type. + * data: Request type specific parameters. + */ +struct mrq_thermal_bpmp_to_host_request { + uint32_t type; + union { + struct cmd_thermal_host_trip_reached_request host_trip_reached; + } __UNION_ANON; +} __ABI_PACKED; + +/* + * Data in reply to a Host->BPMP request. + */ +union mrq_thermal_bpmp_to_host_response { + struct cmd_thermal_get_temp_response get_temp; + struct cmd_thermal_get_num_zones_response get_num_zones; +} __ABI_PACKED; +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_CPU_VHINT + * @brief Query CPU voltage hint data + * + * * Platforms: T186 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: @ref mrq_cpu_vhint_request + * * Response Payload: N/A + * + * @addtogroup Vhint CPU Voltage hint + * @{ + */ + +/** + * @brief request with #MRQ_CPU_VHINT + * + * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data + * from BPMP to memory space pointed by #addr. CCPLEX is responsible + * to allocate sizeof(cpu_vhint_data) sized block of memory and + * appropriately map it for BPMP before sending the request. + */ +struct mrq_cpu_vhint_request { + /** @brief IOVA address for the #cpu_vhint_data */ + uint32_t addr; /* struct cpu_vhint_data * */ + /** @brief ID of the cluster whose data is requested */ + uint32_t cluster_id; /* enum cluster_id */ +} __ABI_PACKED; + +/** + * @brief description of the CPU v/f relation + * + * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of + * struct mrq_cpu_vhint_request + */ +struct cpu_vhint_data { + uint32_t ref_clk_hz; /**< reference frequency in Hz */ + uint16_t pdiv; /**< post divider value */ + uint16_t mdiv; /**< input divider value */ + uint16_t ndiv_max; /**< fMAX expressed with max NDIV value */ + /** table of ndiv values as a function of vINDEX (voltage index) */ + uint16_t ndiv[80]; + /** minimum allowed NDIV value */ + uint16_t ndiv_min; + /** minimum allowed voltage hint value (as in vINDEX) */ + uint16_t vfloor; + /** maximum allowed voltage hint value (as in vINDEX) */ + uint16_t vceil; + /** post-multiplier for vindex value */ + uint16_t vindex_mult; + /** post-divider for vindex value */ + uint16_t vindex_div; + /** reserved for future use */ + uint16_t reserved[328]; +} __ABI_PACKED; + +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_ABI_RATCHET + * @brief ABI ratchet value query + * + * * Platforms: T186 + * * Initiators: Any + * * Targets: BPMP + * * Request Payload: @ref mrq_abi_ratchet_request + * * Response Payload: @ref mrq_abi_ratchet_response + * @addtogroup ABI_info + * @{ + */ + +/** + * @brief an ABI compatibility mechanism + * + * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future + * revision of this header file. + * 1. That future revision deprecates some MRQ + * 2. That future revision introduces a breaking change to an existing + * MRQ or + * 3. A bug is discovered in an existing implementation of the BPMP-FW + * (or possibly one of its clients) which warrants deprecating that + * implementation. + */ +#define BPMP_ABI_RATCHET_VALUE 3 + +/** + * @brief request with #MRQ_ABI_RATCHET. + * + * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header + * against which the requester was compiled. + * + * If ratchet is less than BPMP's #BPMP_ABI_RATCHET_VALUE, BPMP may + * reply with mrq_response::err = -#BPMP_ERANGE to indicate that + * BPMP-FW cannot interoperate correctly with the requester. Requester + * should cease further communication with BPMP. + * + * Otherwise, err shall be 0. + */ +struct mrq_abi_ratchet_request { + /** @brief requester's ratchet value */ + uint16_t ratchet; +}; + +/** + * @brief response to #MRQ_ABI_RATCHET + * + * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header + * against which BPMP firwmare was compiled. + * + * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE, + * the requster must either interoperate with BPMP according to an ABI + * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease + * communication with BPMP. + * + * If mrq_response::err is 0 and ratchet is greater than or equal to the + * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue + * normal operation. + */ +struct mrq_abi_ratchet_response { + /** @brief BPMP's ratchet value */ + uint16_t ratchet; +}; +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_EMC_DVFS_LATENCY + * @brief query frequency dependent EMC DVFS latency + * + * * Platforms: T186 + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: N/A + * * Response Payload: @ref mrq_emc_dvfs_latency_response + * @addtogroup EMC + * @{ + */ + +/** + * @brief used by @ref mrq_emc_dvfs_latency_response + */ +struct emc_dvfs_latency { + /** @brief EMC frequency in kHz */ + uint32_t freq; + /** @brief EMC DVFS latency in nanoseconds */ + uint32_t latency; +} __ABI_PACKED; + +#define EMC_DVFS_LATENCY_MAX_SIZE 14 +/** + * @brief response to #MRQ_EMC_DVFS_LATENCY + */ +struct mrq_emc_dvfs_latency_response { + /** @brief the number valid entries in #pairs */ + uint32_t num_pairs; + /** @brief EMC <frequency, latency> information */ + struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE]; +} __ABI_PACKED; + +/** @} */ + +/** + * @ingroup MRQ_Codes + * @def MRQ_TRACE_ITER + * @brief manage the trace iterator + * + * * Platforms: All + * * Initiators: CCPLEX + * * Targets: BPMP + * * Request Payload: N/A + * * Response Payload: @ref mrq_trace_iter_request + * @addtogroup Trace + * @{ + */ +enum { + /** @brief (re)start the tracing now. Ignore older events */ + TRACE_ITER_INIT = 0, + /** @brief clobber all events in the trace buffer */ + TRACE_ITER_CLEAN = 1 +}; + +/** + * @brief request with #MRQ_TRACE_ITER + */ +struct mrq_trace_iter_request { + /** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */ + uint32_t cmd; +} __ABI_PACKED; + +/** @} */ + +/* + * 4. Enumerations + */ + +/* + * 4.1 CPU enumerations + * + * See <mach-t186/system-t186.h> + * + * 4.2 CPU Cluster enumerations + * + * See <mach-t186/system-t186.h> + * + * 4.3 System low power state enumerations + * + * See <mach-t186/system-t186.h> + */ + +/* + * 4.4 Clock enumerations + * + * For clock enumerations, see <mach-t186/clk-t186.h> + */ + +/* + * 4.5 Reset enumerations + * + * For reset enumerations, see <mach-t186/reset-t186.h> + */ + +/* + * 4.6 Thermal sensor enumerations + * + * For thermal sensor enumerations, see <mach-t186/thermal-t186.h> + */ + +/** + * @defgroup Error_Codes + * Negative values for mrq_response::err generally indicate some + * error. The ABI defines the following error codes. Negating these + * defines is an exercise left to the user. + * @{ + */ +/** @brief No such file or directory */ +#define BPMP_ENOENT 2 +/** @brief No MRQ handler */ +#define BPMP_ENOHANDLER 3 +/** @brief I/O error */ +#define BPMP_EIO 5 +/** @brief Bad sub-MRQ command */ +#define BPMP_EBADCMD 6 +/** @brief Not enough memory */ +#define BPMP_ENOMEM 12 +/** @brief Permission denied */ +#define BPMP_EACCES 13 +/** @brief Bad address */ +#define BPMP_EFAULT 14 +/** @brief No such device */ +#define BPMP_ENODEV 19 +/** @brief Argument is a directory */ +#define BPMP_EISDIR 21 +/** @brief Invalid argument */ +#define BPMP_EINVAL 22 +/** @brief Timeout during operation */ +#define BPMP_ETIMEDOUT 23 +/** @brief Out of range */ +#define BPMP_ERANGE 34 +/** @} */ +/** @} */ +#endif diff --git a/arch/arm/include/asm/arch-tegra/tegra_mmc.h b/arch/arm/include/asm/arch-tegra/tegra_mmc.h index 75e56c4ea7..07ef4c04c8 100644 --- a/arch/arm/include/asm/arch-tegra/tegra_mmc.h +++ b/arch/arm/include/asm/arch-tegra/tegra_mmc.h @@ -9,6 +9,9 @@ #ifndef __TEGRA_MMC_H_ #define __TEGRA_MMC_H_ +#include <common.h> +#include <clk.h> +#include <reset.h> #include <fdtdec.h> #include <asm/gpio.h> @@ -134,7 +137,10 @@ struct mmc_host { int id; /* device id/number, 0-3 */ int enabled; /* 1 to enable, 0 to disable */ int width; /* Bus Width, 1, 4 or 8 */ -#ifndef CONFIG_TEGRA186 +#ifdef CONFIG_TEGRA186 + struct reset_ctl reset_ctl; + struct clk clk; +#else enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */ #endif struct gpio_desc cd_gpio; /* Change Detect GPIO */ diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 85ae3b7f42..1eaf406272 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -64,8 +64,14 @@ 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 diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index d0bf5a6e75..b978fec075 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -25,6 +25,7 @@ obj-y += xusb-padctl-dummy.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 diff --git a/arch/arm/mach-tegra/board186.c b/arch/arm/mach-tegra/board186.c index a7583d4438..1b9799fd80 100644 --- a/arch/arm/mach-tegra/board186.c +++ b/arch/arm/mach-tegra/board186.c @@ -41,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/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/board/nvidia/p2771-0000/p2771-0000.c b/board/nvidia/p2771-0000/p2771-0000.c index 4ba8ebc0dc..529ed9d454 100644 --- a/board/nvidia/p2771-0000/p2771-0000.c +++ b/board/nvidia/p2771-0000/p2771-0000.c @@ -5,3 +5,51 @@ */ #include <common.h> +#include <i2c.h> +#include "../p2571/max77620_init.h" + +int tegra_board_init(void) +{ + struct udevice *dev; + uchar val; + int ret; + + /* Turn on MAX77620 LDO3 to 3.3V for SD card power */ + debug("%s: Set LDO3 for VDDIO_SDMMC_AP power to 3.3V\n", __func__); + ret = i2c_get_chip_for_busnum(0, MAX77620_I2C_ADDR_7BIT, 1, &dev); + if (ret) { + printf("%s: Cannot find MAX77620 I2C chip\n", __func__); + return ret; + } + /* 0xF2 for 3.3v, enabled: bit7:6 = 11 = enable, bit5:0 = voltage */ + val = 0xF2; + ret = dm_i2c_write(dev, MAX77620_CNFG1_L3_REG, &val, 1); + if (ret) { + printf("i2c_write 0 0x3c 0x27 failed: %d\n", ret); + return ret; + } + + return 0; +} + +int tegra_pcie_board_init(void) +{ + struct udevice *dev; + uchar val; + int ret; + + /* Turn on MAX77620 LDO7 to 1.05V for PEX power */ + debug("%s: Set LDO7 for PEX power to 1.05V\n", __func__); + ret = i2c_get_chip_for_busnum(0, MAX77620_I2C_ADDR_7BIT, 1, &dev); + if (ret) { + printf("%s: Cannot find MAX77620 I2C chip\n", __func__); + return -1; + } + /* 0xC5 for 1.05v, enabled: bit7:6 = 11 = enable, bit5:0 = voltage */ + val = 0xC5; + ret = dm_i2c_write(dev, MAX77620_CNFG1_L7_REG, &val, 1); + if (ret) + printf("i2c_write 0 0x3c 0x31 failed: %d\n", ret); + + return 0; +} diff --git a/board/nvidia/venice2/as3722_init.c b/board/nvidia/venice2/as3722_init.c index 960fea7ee7..1770ec2468 100644 --- a/board/nvidia/venice2/as3722_init.c +++ b/board/nvidia/venice2/as3722_init.c @@ -32,7 +32,18 @@ void pmic_enable_cpu_vdd(void) { debug("%s entry\n", __func__); - /* Don't need to set up VDD_CORE - already done - by OTP */ +#ifdef AS3722_SD1VOLTAGE_DATA + /* Set up VDD_CORE, for boards where OTP is incorrect*/ + debug("%s: Setting VDD_CORE via AS3722 reg 1\n", __func__); + /* Configure VDD_CORE via the AS3722 PMIC on the PWR I2C bus */ + tegra_i2c_ll_write_addr(AS3722_I2C_ADDR, 2); + tegra_i2c_ll_write_data(AS3722_SD1VOLTAGE_DATA, I2C_SEND_2_BYTES); + /* + * Don't write SDCONTROL - it's already 0x7F, i.e. all SDs enabled. + * tegra_i2c_ll_write_data(AS3722_SD1CONTROL_DATA, I2C_SEND_2_BYTES); + */ + udelay(10 * 1000); +#endif debug("%s: Setting VDD_CPU to 1.0V via AS3722 reg 0/4D\n", __func__); /* diff --git a/board/nvidia/venice2/as3722_init.h b/board/nvidia/venice2/as3722_init.h index 992b11f643..c6b1247149 100644 --- a/board/nvidia/venice2/as3722_init.h +++ b/board/nvidia/venice2/as3722_init.h @@ -25,8 +25,10 @@ #endif #define AS3722_SD0CONTROL_DATA (0x0100 | AS3722_SDCONTROL_REG) -#define AS3722_SD1VOLTAGE_DATA (0x3200 | AS3722_SD1VOLTAGE_REG) +#ifdef CONFIG_TARGET_JETSON_TK1 +#define AS3722_SD1VOLTAGE_DATA (0x2800 | AS3722_SD1VOLTAGE_REG) #define AS3722_SD1CONTROL_DATA (0x0200 | AS3722_SDCONTROL_REG) +#endif #define AS3722_SD6CONTROL_DATA (0x4000 | AS3722_SDCONTROL_REG) #define AS3722_SD6VOLTAGE_DATA (0x2800 | AS3722_SD6VOLTAGE_REG) diff --git a/configs/p2771-0000-a02_defconfig b/configs/p2771-0000-a02_defconfig index 1fe25f58f1..5ce369c6d3 100644 --- a/configs/p2771-0000-a02_defconfig +++ b/configs/p2771-0000-a02_defconfig @@ -26,6 +26,12 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y +CONFIG_RTL8169=y +CONFIG_E1000=y +CONFIG_PCI_TEGRA=y +CONFIG_TEGRA186_BPMP_I2C=y CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_POWER_DOMAIN=y +CONFIG_TEGRA186_POWER_DOMAIN=y diff --git a/configs/p2771-0000-b00_defconfig b/configs/p2771-0000-b00_defconfig index 552fb6cec7..27393f3a5d 100644 --- a/configs/p2771-0000-b00_defconfig +++ b/configs/p2771-0000-b00_defconfig @@ -26,6 +26,12 @@ CONFIG_CMD_EXT4=y CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y +CONFIG_RTL8169=y +CONFIG_E1000=y +CONFIG_PCI_TEGRA=y +CONFIG_TEGRA186_BPMP_I2C=y CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_DM_USB=y +CONFIG_POWER_DOMAIN=y +CONFIG_TEGRA186_POWER_DOMAIN=y diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6eee8eb369..7dd56738b0 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -20,6 +20,7 @@ config SPL_CLK setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. +source "drivers/clk/tegra/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3cbdd54f4f..463b1d647d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -10,5 +10,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_SANDBOX) += clk_sandbox.o obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_MACH_PIC32) += clk_pic32.o + +obj-y += tegra/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_CLK_EXYNOS) += exynos/ diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig new file mode 100644 index 0000000000..659fe022c2 --- /dev/null +++ b/drivers/clk/tegra/Kconfig @@ -0,0 +1,6 @@ +config TEGRA186_CLOCK + bool "Enable Tegra186 BPMP-based clock driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC clocks via IPC + requests to the BPMP (Boot and Power Management Processor). diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile new file mode 100644 index 0000000000..f32998ccc2 --- /dev/null +++ b/drivers/clk/tegra/Makefile @@ -0,0 +1,5 @@ +# Copyright (c) 2016, NVIDIA CORPORATION. +# +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_TEGRA186_CLOCK) += tegra186-clk.o diff --git a/drivers/clk/tegra/tegra186-clk.c b/drivers/clk/tegra/tegra186-clk.c new file mode 100644 index 0000000000..075cb464cf --- /dev/null +++ b/drivers/clk/tegra/tegra186-clk.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <misc.h> +#include <asm/arch-tegra/bpmp_abi.h> + +static ulong tegra186_clk_get_rate(struct clk *clk) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + req.cmd_and_id = (CMD_CLK_GET_RATE << 24) | clk->id; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return resp.clk_get_rate.rate; +} + +static ulong tegra186_clk_set_rate(struct clk *clk, ulong rate) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + debug("%s(clk=%p, rate=%lu) (dev=%p, id=%lu)\n", __func__, clk, rate, + clk->dev, clk->id); + + req.cmd_and_id = (CMD_CLK_SET_RATE << 24) | clk->id; + req.clk_set_rate.rate = rate; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return resp.clk_set_rate.rate; +} + +static int tegra186_clk_en_dis(struct clk *clk, + enum mrq_reset_commands cmd) +{ + struct mrq_clk_request req; + struct mrq_clk_response resp; + int ret; + + req.cmd_and_id = (cmd << 24) | clk->id; + + ret = misc_call(clk->dev->parent, MRQ_CLK, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_clk_enable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + return tegra186_clk_en_dis(clk, CMD_CLK_ENABLE); +} + +static int tegra186_clk_disable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + return tegra186_clk_en_dis(clk, CMD_CLK_DISABLE); +} + +static struct clk_ops tegra186_clk_ops = { + .get_rate = tegra186_clk_get_rate, + .set_rate = tegra186_clk_set_rate, + .enable = tegra186_clk_enable, + .disable = tegra186_clk_disable, +}; + +static int tegra186_clk_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_clk) = { + .name = "tegra186_clk", + .id = UCLASS_CLK, + .probe = tegra186_clk_probe, + .ops = &tegra186_clk_ops, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index b3e84052eb..67ebec7c57 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -161,6 +161,16 @@ config SYS_I2C_MVTWSI Support for Marvell I2C controllers as used on the orion5x and kirkwood SoC families. +config TEGRA186_BPMP_I2C + bool "Enable Tegra186 BPMP-based I2C driver" + depends on TEGRA186_BPMP + help + Support for Tegra I2C controllers managed by the BPMP (Boot and + Power Management Processor). On Tegra186, some I2C controllers are + directly controlled by the main CPU, whereas others are controlled + by the BPMP, and can only be accessed by the main CPU via IPC + requests to the BPMP. This driver covers the latter case. + source "drivers/i2c/muxes/Kconfig" endmenu diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 167424db98..97b6bedde8 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -41,5 +41,6 @@ obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o +obj-$(CONFIG_TEGRA186_BPMP_I2C) += tegra186_bpmp_i2c.o obj-$(CONFIG_I2C_MUX) += muxes/ diff --git a/drivers/i2c/tegra186_bpmp_i2c.c b/drivers/i2c/tegra186_bpmp_i2c.c new file mode 100644 index 0000000000..88e8413d9e --- /dev/null +++ b/drivers/i2c/tegra186_bpmp_i2c.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> +#include <misc.h> +#include <asm/arch-tegra/bpmp_abi.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct tegra186_bpmp_i2c { + uint32_t bpmp_bus_id; +}; + +static inline void serialize_u16(uint8_t **p, uint16_t val) +{ + (*p)[0] = val & 0xff; + (*p)[1] = val >> 8; + (*p) += 2; +} + +/* These just happen to have the same values as I2C_M_* and SERIALI2C_* */ +#define SUPPORTED_FLAGS \ + (I2C_M_TEN | \ + I2C_M_RD | \ + I2C_M_STOP | \ + I2C_M_NOSTART | \ + I2C_M_REV_DIR_ADDR | \ + I2C_M_IGNORE_NAK | \ + I2C_M_NO_RD_ACK | \ + I2C_M_RECV_LEN) + +static int tegra186_bpmp_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + struct tegra186_bpmp_i2c *priv = dev_get_priv(dev); + struct mrq_i2c_request req; + struct mrq_i2c_response resp; + uint8_t *p; + int left, i, ret; + + req.cmd = CMD_I2C_XFER; + req.xfer.bus_id = priv->bpmp_bus_id; + p = &req.xfer.data_buf[0]; + left = ARRAY_SIZE(req.xfer.data_buf); + for (i = 0; i < nmsgs; i++) { + int len = 6; + if (!(msg[i].flags & I2C_M_RD)) + len += msg[i].len; + if ((len >= BIT(16)) || (len > left)) + return -ENOSPC; + + if (msg[i].flags & ~SUPPORTED_FLAGS) + return -EINVAL; + + serialize_u16(&p, msg[i].addr); + serialize_u16(&p, msg[i].flags); + serialize_u16(&p, msg[i].len); + if (!(msg[i].flags & I2C_M_RD)) { + memcpy(p, msg[i].buf, msg[i].len); + p += msg[i].len; + } + } + req.xfer.data_size = p - &req.xfer.data_buf[0]; + + ret = misc_call(dev->parent, MRQ_I2C, &req, sizeof(req), &resp, + sizeof(resp)); + if (ret < 0) + return ret; + + p = &resp.xfer.data_buf[0]; + left = resp.xfer.data_size; + if (left > ARRAY_SIZE(resp.xfer.data_buf)) + return -EINVAL; + for (i = 0; i < nmsgs; i++) { + if (msg[i].flags & I2C_M_RD) { + memcpy(msg[i].buf, p, msg[i].len); + p += msg[i].len; + } + } + + return 0; +} + +static int tegra186_bpmp_i2c_probe(struct udevice *dev) +{ + struct tegra186_bpmp_i2c *priv = dev_get_priv(dev); + int ret; + struct fdtdec_phandle_args args; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + "nvidia,bpmp", NULL, 0, 0, &args); + if (ret < 0) { + debug("%s: fdtdec_parse_phandle_with_args() failed: %d\n", + __func__, ret); + return ret; + } + + priv->bpmp_bus_id = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "nvidia,bpmp-bus-id", U32_MAX); + if (priv->bpmp_bus_id == U32_MAX) { + debug("%s: could not parse nvidia,bpmp-bus-id\n", __func__); + return -ENODEV; + } + + return 0; +} + +static const struct dm_i2c_ops tegra186_bpmp_i2c_ops = { + .xfer = tegra186_bpmp_i2c_xfer, +}; + +static const struct udevice_id tegra186_bpmp_i2c_ids[] = { + { .compatible = "nvidia,tegra186-bpmp-i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_gpio) = { + .name = "tegra186_bpmp_i2c", + .id = UCLASS_I2C, + .of_match = tegra186_bpmp_i2c_ids, + .probe = tegra186_bpmp_i2c_probe, + .priv_auto_alloc_size = sizeof(struct tegra186_bpmp_i2c), + .ops = &tegra186_bpmp_i2c_ops, +}; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 2fa07f9c57..31ba263b72 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -12,13 +12,27 @@ #include <fdtdec.h> #include <i2c.h> #include <asm/io.h> +#ifdef CONFIG_TEGRA186 +#include <clk.h> +#include <reset.h> +#else #include <asm/arch/clock.h> #include <asm/arch/funcmux.h> -#include <asm/arch/gpio.h> #include <asm/arch/pinmux.h> #include <asm/arch-tegra/clk_rst.h> +#endif +#include <asm/arch/gpio.h> #include <asm/arch-tegra/tegra_i2c.h> +/* + * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that + * should not be present. These are needed because newer Tegra SoCs support + * only the standard clock/reset APIs, whereas older Tegra SoCs support only + * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be + * fixed to implement the standard APIs, and all drivers converted to solely + * use the new standard APIs, with no ifdefs. + */ + DECLARE_GLOBAL_DATA_PTR; enum i2c_type { @@ -30,7 +44,12 @@ enum i2c_type { /* Information about i2c controller */ struct i2c_bus { int id; +#ifdef CONFIG_TEGRA186 + struct reset_ctl reset_ctl; + struct clk clk; +#else enum periph_id periph_id; +#endif int speed; int pinmux_config; struct i2c_control *control; @@ -62,12 +81,41 @@ static void set_packet_mode(struct i2c_bus *i2c_bus) static void i2c_reset_controller(struct i2c_bus *i2c_bus) { /* Reset I2C controller. */ +#ifdef CONFIG_TEGRA186 + reset_assert(&i2c_bus->reset_ctl); + udelay(1); + reset_deassert(&i2c_bus->reset_ctl); + udelay(1); +#else reset_periph(i2c_bus->periph_id, 1); +#endif /* re-program config register to packet mode */ set_packet_mode(i2c_bus); } +#ifdef CONFIG_TEGRA186 +static int i2c_init_clock(struct i2c_bus *i2c_bus, unsigned rate) +{ + int ret; + + ret = reset_assert(&i2c_bus->reset_ctl); + if (ret) + return ret; + ret = clk_enable(&i2c_bus->clk); + if (ret) + return ret; + ret = clk_set_rate(&i2c_bus->clk, rate); + if (IS_ERR_VALUE(ret)) + return ret; + ret = reset_deassert(&i2c_bus->reset_ctl); + if (ret) + return ret; + + return 0; +} +#endif + static void i2c_init_controller(struct i2c_bus *i2c_bus) { if (!i2c_bus->speed) @@ -78,8 +126,12 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) * here, in section 23.3.1, but in fact we seem to need a factor of * 16 to get the right frequency. */ +#ifdef CONFIG_TEGRA186 + i2c_init_clock(i2c_bus, i2c_bus->speed * 2 * 8); +#else clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, i2c_bus->speed * 2 * 8); +#endif if (i2c_bus->type == TYPE_114) { /* @@ -94,12 +146,17 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) * is running, we hang, and we need it for the new calc. */ int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16; + unsigned rate = CLK_MULT_STD_FAST_MODE * + (clk_div_stdfst_mode + 1) * i2c_bus->speed * 2; debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, clk_div_stdfst_mode); +#ifdef CONFIG_TEGRA186 + i2c_init_clock(i2c_bus, rate); +#else clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, - CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) * - i2c_bus->speed * 2); + rate); +#endif } /* Reset I2C controller. */ @@ -112,7 +169,9 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); } +#ifndef CONFIG_TEGRA186 funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config); +#endif } static void send_packet_headers( @@ -333,8 +392,12 @@ static int tegra_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) static int tegra_i2c_probe(struct udevice *dev) { struct i2c_bus *i2c_bus = dev_get_priv(dev); +#ifdef CONFIG_TEGRA186 + int ret; +#else const void *blob = gd->fdt_blob; int node = dev->of_offset; +#endif bool is_dvc; i2c_bus->id = dev->seq; @@ -345,6 +408,18 @@ static int tegra_i2c_probe(struct udevice *dev) * We don't have a binding for pinmux yet. Leave it out for now. So * far no one needs anything other than the default. */ +#ifdef CONFIG_TEGRA186 + ret = reset_get_by_name(dev, "i2c", &i2c_bus->reset_ctl); + if (ret) { + error("reset_get_by_name() failed: %d\n", ret); + return ret; + } + ret = clk_get_by_name(dev, "i2c", &i2c_bus->clk); + if (ret) { + error("clk_get_by_name() failed: %d\n", ret); + return ret; + } +#else i2c_bus->pinmux_config = FUNCMUX_DEFAULT; i2c_bus->periph_id = clock_decode_periph_id(blob, node); @@ -359,6 +434,7 @@ static int tegra_i2c_probe(struct udevice *dev) */ if (i2c_bus->periph_id == -1) return -EINVAL; +#endif is_dvc = dev_get_driver_data(dev) == TYPE_DVC; if (is_dvc) { @@ -370,7 +446,12 @@ static int tegra_i2c_probe(struct udevice *dev) i2c_init_controller(i2c_bus); debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, - i2c_bus->periph_id, i2c_bus->speed); +#ifndef CONFIG_TEGRA186 + i2c_bus->periph_id, +#else + -1, +#endif + i2c_bus->speed); return 0; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 80c15581b9..4af1cbf3d7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -121,6 +121,18 @@ config PCA9551_I2C_ADDR help The I2C address of the PCA9551 LED controller. +config TEGRA186_BPMP + bool "Enable support for the Tegra186 BPMP driver" + depends on TEGRA186 + help + The Tegra BPMP (Boot and Power Management Processor) is a separate + auxiliary CPU embedded into Tegra to perform power management work, + and controls related features such as clocks, resets, power domains, + PMIC I2C bus, etc. This driver provides the core low-level + communication path by which feature-specific drivers (such as clock) + can make requests to the BPMP. This driver is similar to an MFD + driver in the Linux kernel. + config WINBOND_W83627 bool "Enable Winbond Super I/O driver" help diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af541c6784..dac9d1007a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_SANDBOX) += spltest_sandbox.o endif endif obj-$(CONFIG_SANDBOX) += syscon_sandbox.o +obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o diff --git a/drivers/misc/tegra186_bpmp.c b/drivers/misc/tegra186_bpmp.c new file mode 100644 index 0000000000..f4ddbea376 --- /dev/null +++ b/drivers/misc/tegra186_bpmp.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <mailbox.h> +#include <misc.h> +#include <asm/arch-tegra/bpmp_abi.h> +#include <asm/arch-tegra/ivc.h> + +#define BPMP_IVC_FRAME_COUNT 1 +#define BPMP_IVC_FRAME_SIZE 128 + +#define BPMP_FLAG_DO_ACK BIT(0) +#define BPMP_FLAG_RING_DOORBELL BIT(1) + +DECLARE_GLOBAL_DATA_PTR; + +struct tegra186_bpmp { + struct mbox_chan mbox; + struct tegra_ivc ivc; +}; + +static int tegra186_bpmp_call(struct udevice *dev, int mrq, void *tx_msg, + int tx_size, void *rx_msg, int rx_size) +{ + struct tegra186_bpmp *priv = dev_get_priv(dev); + int ret, err; + void *ivc_frame; + struct mrq_request *req; + struct mrq_response *resp; + ulong start_time; + + debug("%s(dev=%p, mrq=%u, tx_msg=%p, tx_size=%d, rx_msg=%p, rx_size=%d) (priv=%p)\n", + __func__, dev, mrq, tx_msg, tx_size, rx_msg, rx_size, priv); + + if ((tx_size > BPMP_IVC_FRAME_SIZE) || (rx_size > BPMP_IVC_FRAME_SIZE)) + return -EINVAL; + + ret = tegra_ivc_write_get_next_frame(&priv->ivc, &ivc_frame); + if (ret) { + error("tegra_ivc_write_get_next_frame() failed: %d\n", ret); + return ret; + } + + req = ivc_frame; + req->mrq = mrq; + req->flags = BPMP_FLAG_DO_ACK | BPMP_FLAG_RING_DOORBELL; + memcpy(req + 1, tx_msg, tx_size); + + ret = tegra_ivc_write_advance(&priv->ivc); + if (ret) { + error("tegra_ivc_write_advance() failed: %d\n", ret); + return ret; + } + + start_time = timer_get_us(); + for (;;) { + ret = tegra_ivc_channel_notified(&priv->ivc); + if (ret) { + error("tegra_ivc_channel_notified() failed: %d\n", ret); + return ret; + } + + ret = tegra_ivc_read_get_next_frame(&priv->ivc, &ivc_frame); + if (!ret) + break; + + /* Timeout 20ms; roughly 10x current max observed duration */ + if ((timer_get_us() - start_time) > 20 * 1000) { + error("tegra_ivc_read_get_next_frame() timed out (%d)\n", + ret); + return -ETIMEDOUT; + } + } + + resp = ivc_frame; + err = resp->err; + if (!err && rx_msg && rx_size) + memcpy(rx_msg, resp + 1, rx_size); + + ret = tegra_ivc_read_advance(&priv->ivc); + if (ret) { + error("tegra_ivc_write_advance() failed: %d\n", ret); + return ret; + } + + if (err) { + error("BPMP responded with error %d\n", err); + /* err isn't a U-Boot error code, so don't that */ + return -EIO; + } + + return rx_size; +} + +/** + * The BPMP exposes multiple different services. We create a sub-device for + * each separate type of service, since each device must be of the appropriate + * UCLASS. + */ +static int tegra186_bpmp_bind(struct udevice *dev) +{ + int ret; + struct udevice *child; + + debug("%s(dev=%p)\n", __func__, dev); + + ret = device_bind_driver_to_node(dev, "tegra186_clk", "tegra186_clk", + dev->of_offset, &child); + if (ret) + return ret; + + ret = device_bind_driver_to_node(dev, "tegra186_reset", + "tegra186_reset", dev->of_offset, + &child); + if (ret) + return ret; + + ret = device_bind_driver_to_node(dev, "tegra186_power_domain", + "tegra186_power_domain", + dev->of_offset, &child); + if (ret) + return ret; + + ret = dm_scan_fdt_dev(dev); + if (ret) + return ret; + + return 0; +} + +static ulong tegra186_bpmp_get_shmem(struct udevice *dev, int index) +{ + int ret; + struct fdtdec_phandle_args args; + fdt_addr_t reg; + + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset, + "shmem", NULL, 0, index, &args); + if (ret < 0) { + error("fdtdec_parse_phandle_with_args() failed: %d\n", ret); + return ret; + } + + reg = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, args.node, + "reg", 0, NULL, true); + if (reg == FDT_ADDR_T_NONE) { + error("fdtdec_get_addr_size_auto_noparent() failed\n"); + return -ENODEV; + } + + return reg; +} + +static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc) +{ + struct tegra186_bpmp *priv = + container_of(ivc, struct tegra186_bpmp, ivc); + int ret; + + ret = mbox_send(&priv->mbox, NULL); + if (ret) + error("mbox_send() failed: %d\n", ret); +} + +static int tegra186_bpmp_probe(struct udevice *dev) +{ + struct tegra186_bpmp *priv = dev_get_priv(dev); + int ret; + ulong tx_base, rx_base, start_time; + + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); + + ret = mbox_get_by_index(dev, 0, &priv->mbox); + if (ret) { + error("mbox_get_by_index() failed: %d\n", ret); + return ret; + } + + tx_base = tegra186_bpmp_get_shmem(dev, 0); + if (IS_ERR_VALUE(tx_base)) { + error("tegra186_bpmp_get_shmem failed for tx_base\n"); + return tx_base; + } + rx_base = tegra186_bpmp_get_shmem(dev, 1); + if (IS_ERR_VALUE(rx_base)) { + error("tegra186_bpmp_get_shmem failed for rx_base\n"); + return rx_base; + } + debug("shmem: rx=%lx, tx=%lx\n", rx_base, tx_base); + + ret = tegra_ivc_init(&priv->ivc, rx_base, tx_base, BPMP_IVC_FRAME_COUNT, + BPMP_IVC_FRAME_SIZE, tegra186_bpmp_ivc_notify); + if (ret) { + error("tegra_ivc_init() failed: %d\n", ret); + return ret; + } + + tegra_ivc_channel_reset(&priv->ivc); + start_time = timer_get_us(); + for (;;) { + ret = tegra_ivc_channel_notified(&priv->ivc); + if (!ret) + break; + + /* Timeout 100ms */ + if ((timer_get_us() - start_time) > 100 * 1000) { + error("Initial IVC reset timed out (%d)\n", ret); + ret = -ETIMEDOUT; + goto err_free_mbox; + } + } + + return 0; + +err_free_mbox: + mbox_free(&priv->mbox); + + return ret; +} + +static int tegra186_bpmp_remove(struct udevice *dev) +{ + struct tegra186_bpmp *priv = dev_get_priv(dev); + + debug("%s(dev=%p) (priv=%p)\n", __func__, dev, priv); + + mbox_free(&priv->mbox); + + return 0; +} + +static struct misc_ops tegra186_bpmp_ops = { + .call = tegra186_bpmp_call, +}; + +static const struct udevice_id tegra186_bpmp_ids[] = { + { .compatible = "nvidia,tegra186-bpmp" }, + { } +}; + +U_BOOT_DRIVER(tegra186_bpmp) = { + .name = "tegra186_bpmp", + .id = UCLASS_MISC, + .of_match = tegra186_bpmp_ids, + .bind = tegra186_bpmp_bind, + .probe = tegra186_bpmp_probe, + .remove = tegra186_bpmp_remove, + .ops = &tegra186_bpmp_ops, + .priv_auto_alloc_size = sizeof(struct tegra186_bpmp), +}; diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 023ba3c643..f0a39a63c6 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -9,6 +9,7 @@ #include <bouncebuf.h> #include <common.h> +#include <dm/device.h> #include <errno.h> #include <asm/gpio.h> #include <asm/io.h> @@ -20,6 +21,15 @@ #include <asm/arch-tegra/tegra_mmc.h> #include <mmc.h> +/* + * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that + * should not be present. These are needed because newer Tegra SoCs support + * only the standard clock/reset APIs, whereas older Tegra SoCs support only + * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be + * fixed to implement the standard APIs, and all drivers converted to solely + * use the new standard APIs, with no ifdefs. + */ + DECLARE_GLOBAL_DATA_PTR; struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; @@ -360,11 +370,14 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) */ if (clock == 0) goto out; -#ifndef CONFIG_TEGRA186 +#ifdef CONFIG_TEGRA186 + { + ulong rate = clk_set_rate(&host->clk, clock); + div = (rate + clock - 1) / clock; + } +#else clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, &div); -#else - div = (20000000 + clock - 1) / clock; #endif debug("div = %d\n", div); @@ -539,6 +552,9 @@ static int do_mmc_init(int dev_index, bool removable) { struct mmc_host *host; struct mmc *mmc; +#ifdef CONFIG_TEGRA186 + int ret; +#endif /* DT should have been read & host config filled in */ host = &mmc_host[dev_index]; @@ -550,7 +566,21 @@ static int do_mmc_init(int dev_index, bool removable) gpio_get_number(&host->cd_gpio)); host->clock = 0; -#ifndef CONFIG_TEGRA186 + +#ifdef CONFIG_TEGRA186 + ret = reset_assert(&host->reset_ctl); + if (ret) + return ret; + ret = clk_enable(&host->clk); + if (ret) + return ret; + ret = clk_set_rate(&host->clk, 20000000); + if (IS_ERR_VALUE(ret)) + return ret; + ret = reset_deassert(&host->reset_ctl); + if (ret) + return ret; +#else clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); #endif @@ -577,11 +607,7 @@ static int do_mmc_init(int dev_index, bool removable) * (actually 52MHz) */ host->cfg.f_min = 375000; -#ifndef CONFIG_TEGRA186 host->cfg.f_max = 48000000; -#else - host->cfg.f_max = 375000; -#endif host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; @@ -613,7 +639,27 @@ static int mmc_get_config(const void *blob, int node, struct mmc_host *host, return -FDT_ERR_NOTFOUND; } -#ifndef CONFIG_TEGRA186 +#ifdef CONFIG_TEGRA186 + { + /* + * FIXME: This variable should go away when the MMC device + * actually is a udevice. + */ + struct udevice dev; + int ret; + dev.of_offset = node; + ret = reset_get_by_name(&dev, "sdmmc", &host->reset_ctl); + if (ret) { + debug("reset_get_by_index() failed: %d\n", ret); + return ret; + } + ret = clk_get_by_name(&dev, "sdmmc", &host->clk); + if (ret) { + debug("clk_get_by_index() failed: %d\n", ret); + return ret; + } + } +#else host->mmc_id = clock_decode_periph_id(blob, node); if (host->mmc_id == PERIPH_ID_NONE) { debug("%s: could not decode periph id\n", __func__); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 26aa2b0930..669e37bb5d 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -31,6 +31,7 @@ config PCI_SANDBOX config PCI_TEGRA bool "Tegra PCI support" depends on TEGRA + depends on (TEGRA186 && POWER_DOMAIN) || (!TEGRA186) help Enable support for the PCIe controller found on some generations of Tegra. Tegra20 has 2 root ports with a total of 4 lanes, Tegra30 has diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 352cdef56a..ea8adb98db 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -13,22 +13,35 @@ #define pr_fmt(fmt) "tegra-pcie: " fmt #include <common.h> +#include <clk.h> #include <dm.h> #include <errno.h> #include <fdtdec.h> #include <malloc.h> #include <pci.h> +#include <power-domain.h> +#include <reset.h> #include <asm/io.h> #include <asm/gpio.h> +#include <linux/list.h> + +#ifndef CONFIG_TEGRA186 #include <asm/arch/clock.h> #include <asm/arch/powergate.h> #include <asm/arch-tegra/xusb-padctl.h> - -#include <linux/list.h> - #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> +#endif + +/* + * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that + * should not be present. These are needed because newer Tegra SoCs support + * only the standard clock/reset APIs, whereas older Tegra SoCs support only + * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be + * fixed to implement the standard APIs, and all drivers converted to solely + * use the new standard APIs, with no ifdefs. + */ DECLARE_GLOBAL_DATA_PTR; @@ -103,6 +116,9 @@ DECLARE_GLOBAL_DATA_PTR; #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_401 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_211 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_111 (0x2 << 20) #define AFI_FUSE 0x104 #define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) @@ -110,6 +126,7 @@ DECLARE_GLOBAL_DATA_PTR; #define AFI_PEX0_CTRL 0x110 #define AFI_PEX1_CTRL 0x118 #define AFI_PEX2_CTRL 0x128 +#define AFI_PEX2_CTRL_T186 0x19c #define AFI_PEX_CTRL_RST (1 << 0) #define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) #define AFI_PEX_CTRL_REFCLK_EN (1 << 3) @@ -173,6 +190,7 @@ enum tegra_pci_id { TEGRA30_PCIE, TEGRA124_PCIE, TEGRA210_PCIE, + TEGRA186_PCIE, }; struct tegra_pcie_port { @@ -189,6 +207,7 @@ struct tegra_pcie_soc { unsigned int num_ports; unsigned long pads_pll_ctl; unsigned long tx_ref_sel; + unsigned long afi_pex2_ctrl; u32 pads_refclk_cfg0; u32 pads_refclk_cfg1; bool has_pex_clkreq_en; @@ -209,7 +228,17 @@ struct tegra_pcie { unsigned long xbar; const struct tegra_pcie_soc *soc; + +#ifdef CONFIG_TEGRA186 + struct clk clk_afi; + struct clk clk_pex; + struct reset_ctl reset_afi; + struct reset_ctl reset_pex; + struct reset_ctl reset_pcie_x; + struct power_domain pwrdom; +#else struct tegra_xusb_phy *phy; +#endif }; static void afi_writel(struct tegra_pcie *pcie, unsigned long value, @@ -229,10 +258,12 @@ static void pads_writel(struct tegra_pcie *pcie, unsigned long value, writel(value, pcie->pads.start + offset); } +#ifndef CONFIG_TEGRA186 static unsigned long pads_readl(struct tegra_pcie *pcie, unsigned long offset) { return readl(pcie->pads.start + offset); } +#endif static unsigned long rp_readl(struct tegra_pcie_port *port, unsigned long offset) @@ -400,6 +431,24 @@ static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, return 0; } break; + case TEGRA186_PCIE: + switch (lanes) { + case 0x0010004: + debug("x4 x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_401; + return 0; + + case 0x0010102: + debug("x2 x1 x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_211; + return 0; + + case 0x0010101: + debug("x1 x1 x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_T186_111; + return 0; + } + break; default: break; } @@ -471,6 +520,7 @@ static int tegra_pcie_parse_dt(const void *fdt, int node, enum tegra_pci_id id, return err; } +#ifndef CONFIG_TEGRA186 pcie->phy = tegra_xusb_phy_get(TEGRA_XUSB_PADCTL_PCIE); if (pcie->phy) { err = tegra_xusb_phy_prepare(pcie->phy); @@ -479,6 +529,7 @@ static int tegra_pcie_parse_dt(const void *fdt, int node, enum tegra_pci_id id, return err; } } +#endif fdt_for_each_subnode(fdt, subnode, node) { unsigned int index = 0, num_lanes = 0; @@ -523,6 +574,44 @@ static int tegra_pcie_parse_dt(const void *fdt, int node, enum tegra_pci_id id, return 0; } +#ifdef CONFIG_TEGRA186 +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + int ret; + + ret = power_domain_on(&pcie->pwrdom); + if (ret) { + error("power_domain_on() failed: %d\n", ret); + return ret; + } + + ret = clk_enable(&pcie->clk_afi); + if (ret) { + error("clk_enable(afi) failed: %d\n", ret); + return ret; + } + + ret = clk_enable(&pcie->clk_pex); + if (ret) { + error("clk_enable(pex) failed: %d\n", ret); + return ret; + } + + ret = reset_deassert(&pcie->reset_afi); + if (ret) { + error("reset_deassert(afi) failed: %d\n", ret); + return ret; + } + + ret = reset_deassert(&pcie->reset_pex); + if (ret) { + error("reset_deassert(pex) failed: %d\n", ret); + return ret; + } + + return 0; +} +#else static int tegra_pcie_power_on(struct tegra_pcie *pcie) { const struct tegra_pcie_soc *soc = pcie->soc; @@ -639,6 +728,7 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) return 0; } +#endif static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) { @@ -647,7 +737,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) u32 value; int err; +#ifdef CONFIG_TEGRA186 + { +#else if (pcie->phy) { +#endif value = afi_readl(pcie, AFI_PLLE_CONTROL); value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; @@ -675,6 +769,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); +#ifndef CONFIG_TEGRA186 if (pcie->phy) err = tegra_xusb_phy_enable(pcie->phy); else @@ -684,9 +779,18 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) error("failed to power on PHY: %d\n", err); return err; } +#endif /* take the PCIEXCLK logic out of reset */ +#ifdef CONFIG_TEGRA186 + err = reset_deassert(&pcie->reset_pcie_x); + if (err) { + error("reset_deassert(pcie_x) failed: %d\n", err); + return err; + } +#else reset_set_enable(PERIPH_ID_PCIEXCLK, 0); +#endif /* finally enable PCIe */ value = afi_readl(pcie, AFI_CONFIGURATION); @@ -787,7 +891,7 @@ static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) break; case 2: - ret = AFI_PEX2_CTRL; + ret = port->pcie->soc->afi_pex2_ctrl; break; } @@ -945,6 +1049,7 @@ static const struct tegra_pcie_soc pci_tegra_soc[] = { .num_ports = 3, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .afi_pex2_ctrl = AFI_PEX2_CTRL, .pads_refclk_cfg0 = 0xfa5cfa5c, .pads_refclk_cfg1 = 0xfa5cfa5c, .has_pex_clkreq_en = true, @@ -972,7 +1077,16 @@ static const struct tegra_pcie_soc pci_tegra_soc[] = { .has_cml_clk = true, .has_gen2 = true, .force_pca_enable = true, - } + }, + [TEGRA186_PCIE] = { + .num_ports = 3, + .afi_pex2_ctrl = AFI_PEX2_CTRL_T186, + .pads_refclk_cfg0 = 0x80b880b8, + .pads_refclk_cfg1 = 0x000480b8, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_gen2 = true, + }, }; static int pci_tegra_ofdata_to_platdata(struct udevice *dev) @@ -996,6 +1110,44 @@ static int pci_tegra_probe(struct udevice *dev) struct tegra_pcie *pcie = dev_get_priv(dev); int err; +#ifdef CONFIG_TEGRA186 + err = clk_get_by_name(dev, "afi", &pcie->clk_afi); + if (err) { + debug("clk_get_by_name(afi) failed: %d\n", err); + return err; + } + + err = clk_get_by_name(dev, "pex", &pcie->clk_pex); + if (err) { + debug("clk_get_by_name(pex) failed: %d\n", err); + return err; + } + + err = reset_get_by_name(dev, "afi", &pcie->reset_afi); + if (err) { + debug("reset_get_by_name(afi) failed: %d\n", err); + return err; + } + + err = reset_get_by_name(dev, "pex", &pcie->reset_pex); + if (err) { + debug("reset_get_by_name(pex) failed: %d\n", err); + return err; + } + + err = reset_get_by_name(dev, "pcie_x", &pcie->reset_pcie_x); + if (err) { + debug("reset_get_by_name(pcie_x) failed: %d\n", err); + return err; + } + + err = power_domain_get(dev, &pcie->pwrdom); + if (err) { + debug("power_domain_get() failed: %d\n", err); + return err; + } +#endif + err = tegra_pcie_power_on(pcie); if (err < 0) { error("failed to power on"); @@ -1033,6 +1185,7 @@ static const struct udevice_id pci_tegra_ids[] = { { .compatible = "nvidia,tegra30-pcie", .data = TEGRA30_PCIE }, { .compatible = "nvidia,tegra124-pcie", .data = TEGRA124_PCIE }, { .compatible = "nvidia,tegra210-pcie", .data = TEGRA210_PCIE }, + { .compatible = "nvidia,tegra186-pcie", .data = TEGRA186_PCIE }, { } }; diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index b904097433..132e33250e 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -17,4 +17,11 @@ config SANDBOX_POWER_DOMAIN simply accepts requests to power on/off various HW modules without actually doing anything beyond a little error checking. +config TEGRA186_POWER_DOMAIN + bool "Enable Tegra186 BPMP-based power domain driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC power domains via IPC + requests to the BPMP (Boot and Power Management Processor). + endmenu diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index c18292f0ec..2c3d92638f 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o +obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o diff --git a/drivers/power/domain/tegra186-power-domain.c b/drivers/power/domain/tegra186-power-domain.c new file mode 100644 index 0000000000..41d84de83e --- /dev/null +++ b/drivers/power/domain/tegra186-power-domain.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <misc.h> +#include <power-domain-uclass.h> +#include <asm/arch-tegra/bpmp_abi.h> + +#define UPDATE BIT(0) +#define ON BIT(1) + +static int tegra186_power_domain_request(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return 0; +} + +static int tegra186_power_domain_free(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return 0; +} + +static int tegra186_power_domain_common(struct power_domain *power_domain, + bool on) +{ + struct mrq_pg_update_state_request req; + int on_state = on ? ON : 0; + int ret; + + req.partition_id = power_domain->id; + req.logic_state = UPDATE | on_state; + req.sram_state = UPDATE | on_state; + /* + * Drivers manage their own clocks so they don't get out of sync, and + * since some power domains have many clocks, only a subset of which + * are actually needed depending on use-case. + */ + req.clock_state = UPDATE; + + ret = misc_call(power_domain->dev->parent, MRQ_PG_UPDATE_STATE, &req, + sizeof(req), NULL, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_power_domain_on(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return tegra186_power_domain_common(power_domain, true); +} + +static int tegra186_power_domain_off(struct power_domain *power_domain) +{ + debug("%s(power_domain=%p) (dev=%p, id=%lu)\n", __func__, + power_domain, power_domain->dev, power_domain->id); + + return tegra186_power_domain_common(power_domain, false); +} + +struct power_domain_ops tegra186_power_domain_ops = { + .request = tegra186_power_domain_request, + .free = tegra186_power_domain_free, + .on = tegra186_power_domain_on, + .off = tegra186_power_domain_off, +}; + +static int tegra186_power_domain_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_power_domain) = { + .name = "tegra186_power_domain", + .id = UCLASS_POWER_DOMAIN, + .probe = tegra186_power_domain_probe, + .ops = &tegra186_power_domain_ops, +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 0fe8cc3827..5b84f2178b 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -20,4 +20,11 @@ config SANDBOX_RESET simply accepts requests to reset various HW modules without actually doing anything beyond a little error checking. +config TEGRA186_RESET + bool "Enable Tegra186 BPMP-based reset driver" + depends on TEGRA186_BPMP + help + Enable support for manipulating Tegra's on-SoC reset signals via IPC + requests to the BPMP (Boot and Power Management Processor). + endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 71f3b21961..ff0e090775 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_DM_RESET) += reset-uclass.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o +obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o diff --git a/drivers/reset/tegra186-reset.c b/drivers/reset/tegra186-reset.c new file mode 100644 index 0000000000..228adda0aa --- /dev/null +++ b/drivers/reset/tegra186-reset.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <misc.h> +#include <reset-uclass.h> +#include <asm/arch-tegra/bpmp_abi.h> + +static int tegra186_reset_request(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return 0; +} + +static int tegra186_reset_free(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return 0; +} + +static int tegra186_reset_common(struct reset_ctl *reset_ctl, + enum mrq_reset_commands cmd) +{ + struct mrq_reset_request req; + int ret; + + req.cmd = cmd; + req.reset_id = reset_ctl->id; + + ret = misc_call(reset_ctl->dev->parent, MRQ_RESET, &req, sizeof(req), + NULL, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int tegra186_reset_assert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return tegra186_reset_common(reset_ctl, CMD_RESET_ASSERT); +} + +static int tegra186_reset_deassert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return tegra186_reset_common(reset_ctl, CMD_RESET_DEASSERT); +} + +struct reset_ops tegra186_reset_ops = { + .request = tegra186_reset_request, + .free = tegra186_reset_free, + .rst_assert = tegra186_reset_assert, + .rst_deassert = tegra186_reset_deassert, +}; + +static int tegra186_reset_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra186_reset) = { + .name = "tegra186_reset", + .id = UCLASS_RESET, + .probe = tegra186_reset_probe, + .ops = &tegra186_reset_ops, +}; diff --git a/include/configs/p2771-0000.h b/include/configs/p2771-0000.h index 257283f3b9..1f64405f11 100644 --- a/include/configs/p2771-0000.h +++ b/include/configs/p2771-0000.h @@ -14,6 +14,9 @@ /* High-level configuration options */ #define CONFIG_TEGRA_BOARD_STRING "NVIDIA P2771-0000" +/* I2C */ +#define CONFIG_SYS_I2C_TEGRA + /* SD/MMC */ #define CONFIG_MMC #define CONFIG_GENERIC_MMC @@ -25,6 +28,11 @@ #define CONFIG_SYS_MMC_ENV_PART 2 #define CONFIG_ENV_OFFSET (-CONFIG_ENV_SIZE) +/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI + #include "tegra-common-post.h" /* Crystal is 38.4MHz. clk_m runs at half that rate */ diff --git a/include/configs/trimslice.h b/include/configs/trimslice.h index b761640b8f..9d49f63803 100644 --- a/include/configs/trimslice.h +++ b/include/configs/trimslice.h @@ -37,7 +37,8 @@ #define CONFIG_ENV_SPI_MAX_HZ 48000000 #define CONFIG_ENV_SPI_MODE SPI_MODE_0 #define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE -#define CONFIG_ENV_OFFSET (512 * 1024) +/* 1MiB flash, environment located as high as possible */ +#define CONFIG_ENV_OFFSET (SZ_1M - CONFIG_ENV_SIZE) /* USB Host support */ #define CONFIG_USB_EHCI |