summaryrefslogtreecommitdiff
path: root/src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c
diff options
context:
space:
mode:
authorKerry Sheh <shekairui@gmail.com>2012-01-31 20:39:37 +0800
committerPatrick Georgi <patrick@georgi-clan.de>2012-02-02 13:54:36 +0100
commit9292d89be84d6abf9257ddb872887d4f53b2a00e (patch)
tree9eaa548f1742745f57fc92a12734649fec8db1cd /src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c
parent17670866a0d12839bc2a4c852210ccf11d3cb4b2 (diff)
RD890 Northbridge: AMD RD890/SR56X0 Northbridge CIMX code
Change-Id: If9908ffeb5b707a660db38dc44f5118347cbcc06 Signed-off-by: Kerry Sheh <kerry.she@amd.com> Signed-off-by: Kerry Sheh <shekairui@gmail.com> Reviewed-on: http://review.coreboot.org/557 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c')
-rw-r--r--src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c b/src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c
new file mode 100644
index 0000000000..17ae4f746b
--- /dev/null
+++ b/src/vendorcode/amd/cimx/rd890/nbPcieInitLate.c
@@ -0,0 +1,505 @@
+/**
+ * @file
+ *
+ * PCIE Late Initialization
+ *
+ *
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: CIMx-NB
+ * @e sub-project:
+ * @e \$Revision:$ @e \$Date:$
+ *
+ */
+/*****************************************************************************
+ *
+ * Copyright (C) 2012 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Advanced Micro Devices, Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ ***************************************************************************/
+
+/*----------------------------------------------------------------------------------------
+ * M O D U L E S U S E D
+ *----------------------------------------------------------------------------------------
+ */
+
+#include "NbPlatform.h"
+#include "amdDebugOutLib.h"
+#include "amdSbLib.h"
+
+/*----------------------------------------------------------------------------------------
+ * D E F I N I T I O N S A N D M A C R O S
+ *----------------------------------------------------------------------------------------
+ */
+
+
+/*----------------------------------------------------------------------------------------
+ * T Y P E D E F S A N D S T R U C T U R E S
+ *----------------------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------------------
+ * P R O T O T Y P E S O F L O C A L F U N C T I O N S
+ *----------------------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------------------
+ * E X P O R T E D F U N C T I O N S
+ *----------------------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Amd PCIE Late Init for all NB.
+ *
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ *
+ */
+AGESA_STATUS
+AmdPcieLateInit (
+ IN AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = LibNbApiCall (PcieLateInit, ConfigPtr);
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Amd PCIE Late Init for all NB.
+ *
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ *
+ */
+AGESA_STATUS
+AmdPcieLateInitWa (
+ IN AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = LibNbApiCall (PcieLateInitWa, ConfigPtr);
+ return Status;
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Amd PCIE Special Init for all NB.
+ *
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ *
+ */
+AGESA_STATUS
+AmdPcieValidatePortState (
+ IN AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = LibNbApiCall (PcieValidatePortState, ConfigPtr);
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Amd PCIE S3 Init fro all NB.
+ *
+ *
+ * @param[in] ConfigPtr Northbridges configuration block pointer.
+ *
+ */
+AGESA_STATUS
+AmdPcieS3Init (
+ IN AMD_NB_CONFIG_BLOCK *ConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = LibNbApiCall (PcieLateInit, ConfigPtr);
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * NB PCIE Late Init.
+ * Extended programming. Enable power management and misc capability.
+ * Prepare PCIE subsystem to boot to OS.
+ *
+ *
+ * @param[in] NbConfigPtr Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateInit (
+ IN AMD_NB_CONFIG *NbConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+ PcieLibUnHidePorts (NbConfigPtr);
+ Status = PcieLateValidateConfiguration (NbConfigPtr);
+ if (Status == AGESA_FATAL) {
+ PcieLibHidePorts (NbConfigPtr);
+ REPORT_EVENT (AGESA_FATAL, GENERAL_ERROR_BAD_CONFIGURATION, 0, 0, 0, 0, NbConfigPtr);
+ CIMX_ASSERT (FALSE);
+ return Status;
+ }
+ PcieLibLateInit (NbConfigPtr);
+ Status = PcieLateInitPorts (NbConfigPtr);
+ Status = PcieLateInitCores (NbConfigPtr);
+ PcieLibHidePorts (NbConfigPtr);
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * NB PCIE Late Init.
+ * Extended programming. Enable power management and misc capability.
+ * Prepare PCIE subsystem to boot to OS.
+ *
+ *
+ * @param[in] NbConfigPtr Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateInitWa (
+ IN AMD_NB_CONFIG *NbConfigPtr
+ )
+{
+ UINT32 Value;
+ BOOLEAN SmuWa;
+ LibNbPciIndexRead (NbConfigPtr->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG4A, AccessWidth32, &Value, NbConfigPtr);
+ SmuWa = ((Value & BIT21) != 0) ? TRUE : FALSE;
+ if (SmuWa) {
+ UINT32 SmuWaData;
+ LibNbMcuControl (AssertReset, NbConfigPtr);
+ SmuWaData = LibNbReadMcuRam (0xFE74, NbConfigPtr);
+ SmuWaData &= 0x00ff;
+ LibNbLoadMcuFirmwareBlock (0xFE74, 0x1, &SmuWaData, NbConfigPtr);
+ LibNbMcuControl (DeAssertReset, NbConfigPtr);
+ }
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Late init PCIE Ports
+ *
+ *
+ *
+* @param[in] NbConfigPtr Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateInitPorts (
+ IN AMD_NB_CONFIG *NbConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+ PCIE_CONFIG *pPcieConfig;
+ PORT PortId;
+ BOOLEAN IsIommuEnabled;
+ NB_INFO NbInfo;
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (NbConfigPtr), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateInitPorts Enter\n"));
+ IsIommuEnabled = LibNbIsIommuEnabled (NbConfigPtr);
+ NbInfo = LibNbGetRevisionInfo (NbConfigPtr);
+ pPcieConfig = GET_PCIE_CONFIG_PTR (NbConfigPtr);
+ Status = AGESA_SUCCESS;
+ for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
+ CORE CoreId;
+ CoreId = PcieLibGetCoreId (PortId, NbConfigPtr);
+ if (!PcieLibIsValidCoreId (CoreId, NbConfigPtr)) {
+ PcieLibPowerOffPortLanes (PortId, PcieLinkWidth_x0, NbConfigPtr);
+ } else if (PcieLibIsValidPortId (PortId, NbConfigPtr)) {
+ PCIE_LINK_WIDTH LinkWidth;
+ PCI_ADDR Port;
+ LinkWidth = PcieLibGetLinkWidth (PortId, NbConfigPtr);
+ CoreId = PcieLibGetCoreId (PortId, NbConfigPtr);
+ Port = PcieLibGetPortPciAddress (PortId, NbConfigPtr);
+ PcieLateCommonPortInit (PortId, NbConfigPtr);
+ if (pPcieConfig->PortConfiguration[PortId].PortDetected == ON) {
+ if (pPcieConfig->PortConfiguration[PortId].PortLinkMode == PcieLinkModeGen2SoftwareInitiated) {
+ PcieInitiateSoftwareGen2 (PortId, NbConfigPtr);
+ }
+ PcieAsmpEnableOnPort (PortId, (UINT8)pPcieConfig->PortConfiguration[PortId].PortAspm, NbConfigPtr);
+ }
+ LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG70 , AccessS3SaveWidth32, (UINT32)~BIT12, 0, NbConfigPtr); //PCIE should not ignore malformed packet error or ATS request
+ if (pPcieConfig->PortConfiguration[PortId].PortCompliance == OFF &&
+ pPcieConfig->PortConfiguration[PortId].PortHotplug == OFF &&
+ pPcieConfig->CoreSetting[CoreId].PowerOffUnusedLanes == ON) {
+ PcieLibPowerOffPortLanes (PortId, LinkWidth, NbConfigPtr);
+ }
+ }
+ }
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (NbConfigPtr), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateInitPorts Exit\n"));
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Late init PCIE Cores. Core level feature/power management etc.
+ *
+ *
+ *
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateInitCores (
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ AGESA_STATUS Status;
+ PCIE_CONFIG *pPcieConfig;
+ CORE CoreId;
+
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateInitCores Enter\n"));
+ Status = AGESA_SUCCESS;
+ pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
+ for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " Init CoreId [%d]\n", CoreId));
+ if (pPcieConfig->CoreSetting[CoreId].PowerOffPllInL1 == ON) {
+ PcieLibEnablePllPowerOffInL1 (CoreId, pConfig);
+ }
+ if (pPcieConfig->CoreSetting[CoreId].PowerOffPll == ON) {
+ PcieLibPowerOffPll (CoreId, pConfig);
+ }
+ PcieLibMiscLateCoreSetting (CoreId, pConfig);
+ PcieLibManageTxClock (CoreId, pConfig);
+ PcieLibManageLclkClock (CoreId, pConfig);
+ }
+#ifndef VC1_SUPPORT_DISABLE
+ if (NB_SBDFO == 0 && pPcieConfig->PcieConfiguration.NbSbVc1 == ON) {
+ PcieNbSbSetupVc (pConfig);
+ }
+#endif
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateInitCores Exit\n"));
+ return Status;
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/*
+ * Set up NB-SB virtual channel for audio traffic
+ *
+ *
+ *
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+VOID
+PcieNbSbSetupVc (
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT32 VCStatus;
+ PCI_ADDR Port;
+
+ Port = PcieLibGetPortPciAddress (8, pConfig);
+ if (PcieSbSetupVc (pConfig) == AGESA_SUCCESS) {
+ LibNbPciRMW (Port.AddressValue | NB_PCIP_REG124, AccessS3SaveWidth8, 0x01, 0, pConfig);
+ LibNbPciRMW (Port.AddressValue | NB_PCIP_REG130, AccessS3SaveWidth32, (UINT32)~(BIT24 + BIT25 + BIT26), 0xFE + BIT24, pConfig);
+ LibNbPciRMW (Port.AddressValue | NB_PCIP_REG130, AccessS3SaveWidth32, 0xffffffff, BIT31, pConfig);
+ do {
+ STALL (GET_BLOCK_CONFIG_PTR (pConfig), 200, CIMX_S3_SAVE);
+ LibNbPciRead (Port.AddressValue | NB_PCIP_REG134, AccessWidth32, &VCStatus, pConfig);
+ } while (VCStatus & BIT17);
+ PcieSbEnableVc (pConfig);
+ }
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/*
+ * Late common Port Init
+ *
+ *
+ * @param[in] PortId Port Id
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateCommonPortInit (
+ IN PORT PortId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ AGESA_STATUS Status;
+ Status = AGESA_SUCCESS;
+
+ return Status;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/*
+ * Initiate SW Gen2 switch
+ *
+ *
+ *
+ * @param[in] PortId Port Id.
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+VOID
+PcieInitiateSoftwareGen2 (
+ IN PORT PortId,
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ UINT8 LinkSpeedCap;
+ UINT8 PcieCapPtr;
+ UINT8 SecondaryBus;
+ UINT32 Value;
+ UINT32 Counter;
+ PCI_ADDR Ep;
+ PCI_ADDR Port;
+
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieInitiateSoftwareGen2 PortId[%d] Enter\n", PortId));
+ Counter = 5000;
+ Port = PcieLibGetPortPciAddress (PortId, pConfig);
+ LibNbPciRead (Port.AddressValue | NB_PCIP_REG19, AccessWidth8, &SecondaryBus, pConfig);
+ Ep.AddressValue = 0;
+ Ep.Address.Bus = SecondaryBus;
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE] SecondaryBus = 0x%x \n", SecondaryBus));
+ PcieCapPtr = LibNbFindPciCapability (Ep.AddressValue, PCIE_CAP_ID, pConfig);
+ LibNbPciRead (Ep.AddressValue | (PcieCapPtr + 0xC), AccessWidth8, &LinkSpeedCap, pConfig);
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE] PcieCapPtr = 0x%x \n", PcieCapPtr));
+ if ((LinkSpeedCap & 0xf) < 2) {
+ return;
+ }
+ PcieLibSetLinkMode (PortId, PcieLinkModeGen2, pConfig);
+ LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGA4 , AccessS3SaveWidth32, (UINT32)~(BIT18), BIT18 , pConfig);
+ do {
+ STALL (GET_BLOCK_CONFIG_PTR (pConfig), 200, CIMX_S3_SAVE);
+ LibNbPciIndexRead (Port.AddressValue | NB_PCIP_REGE0, NB_BIFNBP_REGA5, AccessWidth32, &Value, pConfig);
+ } while ((UINT8)Value != 0x10 && Counter-- != 0);
+ LibNbPciIndexRead (Port.AddressValue | NB_PCIP_REGE0, NB_BIFNBP_REGA4, AccessWidth32, &Value, pConfig);
+ if ((Value & BIT24) != 0) {
+ //Initiate link speed change
+ LibNbPciIndexRMW (Port.AddressValue | NB_PCIP_REGE0, NB_BIFNBP_REGA4, AccessS3SaveWidth32, ((UINT32)~BIT7), BIT7, pConfig);
+ }
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieInitiateSoftwareGen2 Exit\n"));
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * Validate input parameters configuration for PCie Late Init call.
+ *
+ *
+ *
+ * @param[in] pConfig Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieLateValidateConfiguration (
+ IN AMD_NB_CONFIG *pConfig
+ )
+{
+ PCIE_CONFIG *pPcieConfig;
+ NB_INFO NbInfo;
+
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateValidateConfiguration Enter\n"));
+ pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
+ NbInfo = LibNbGetRevisionInfo (pConfig);
+ if (pPcieConfig == NULL) {
+ REPORT_EVENT (AGESA_FATAL, GENERAL_ERROR_BAD_CONFIGURATION, 0, 0, 0, 0, pConfig);
+ CIMX_ASSERT (FALSE);
+ return AGESA_FATAL;
+ }
+ if (pPcieConfig->sHeader.InitializerID != INITIALIZED_BY_INITIALIZER) {
+ PcieLibInitializer (pConfig);
+ }
+ CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLateValidateConfiguration Exit\n"));
+ return AGESA_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * PcieValidatePortState
+ * Port disable or port visibility control
+ *
+ *
+ * @param[in] NbConfigPtr Northbridge configuration structure pointer.
+ *
+ */
+AGESA_STATUS
+PcieValidatePortState (
+ IN AMD_NB_CONFIG *NbConfigPtr
+ )
+{
+ AGESA_STATUS Status;
+
+ Status = AGESA_SUCCESS;
+ PcieLibUnHidePorts (NbConfigPtr);
+ PcieLibValidatePortStateInit (NbConfigPtr);
+ PcieForcePortsVisibleOrDisable (NbConfigPtr);
+ PcieLibHidePorts (NbConfigPtr);
+ return Status;
+}
+
+
+/*----------------------------------------------------------------------------------------*/
+/**
+ * PciePortsVisibleOrDisable
+ * Set ports always visible or disable based on input parameter
+ *
+ *
+ *
+* @param[in] NbConfigPtr Northbridge configuration structure pointer.
+ *
+ */
+VOID
+PcieForcePortsVisibleOrDisable (
+ IN AMD_NB_CONFIG *NbConfigPtr
+ )
+{
+ PCIE_CONFIG *pPcieConfig;
+ PORT PortId;
+ PCI_ADDR Port;
+
+ pPcieConfig = GET_PCIE_CONFIG_PTR (NbConfigPtr);
+ for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
+ if (PcieLibIsValidPortId (PortId, NbConfigPtr)) {
+ Port = PcieLibGetPortPciAddress (PortId, NbConfigPtr);
+ if (pPcieConfig->PortConfiguration[PortId].ForcePortDisable == ON ) {
+ pPcieConfig->PortConfiguration[PortId].PortPresent = OFF;
+ pPcieConfig->PortConfiguration[PortId].PortDetected = OFF;
+ }
+ if (pPcieConfig->PortConfiguration[PortId].PortAlwaysVisible == ON) {
+ LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG70, AccessWidth32, (UINT32)~BIT19, BIT19, NbConfigPtr);
+ pPcieConfig->PortConfiguration[PortId].PortPresent = ON;
+ pPcieConfig->PortConfiguration[PortId].PortDetected = ON;
+ }
+ LibNbPciIndexWrite (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG01, AccessWidth32, (UINT32*)&pPcieConfig->PortConfiguration[PortId], NbConfigPtr);
+ }
+ }
+}
+