/** SerDes.c
  Provides SoC specific SerDes interface

  Copyright 2020 NXP

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Library/DebugLib.h>
#include <Library/SerDesHelperLib.h>

/**
  Function to get SerDes Lane protocol corresponding to
  SerDes protocol.

  @param  SerDes              SerDes number.
  @param  SerDesProtocol      SerDes protocol number.
  @param  Lane                SerDes Lane number.
  @param  SerDesMaxProtocol   Max SerDes protocol number.
  @param  Config              SerDes Configuration.

  @return SerDes Lane protocol.

**/
UINT32
GetSerDesProtocol (
  IN  INTN            SerDes,
  IN  INTN            SerDesProtocol,
  IN  INTN            Lane,
  IN  UINT32          SerDesMaxProtocol,
  IN  SERDES_CONFIG   *Config
  )
{
  while (Config->Protocol) {
    if (Config->Protocol == SerDesProtocol) {
      return Config->SerDesLane[Lane];
    }
    Config++;
  }

  return SerDesMaxProtocol;
}

/**
  Function to validate input SerDes protocol.

  @param  SerDes              SerDes number.
  @param  SerDesProtocol      SerDes protocol number.
  @param  SerDesNumLanes      Number of SerDes Lanes.
  @param  Config              SerDes Configuration.

  @return EFI_NOT_FOUND     SerDes Protocol not a valid protocol.
  @return EFI_SUCCESS       SerDes Protocol is a valid protocol.

**/
EFI_STATUS
IsSerDesProtocolValid (
  IN  INTN           SerDes,
  IN  UINT32         SerDesProtocol,
  IN  UINT8          SerDesNumLanes,
  IN  SERDES_CONFIG  *Config
  )
{
  UINT8 Count;

  while (Config->Protocol) {
    if (Config->Protocol == SerDesProtocol) {
      DEBUG ((DEBUG_INFO, "Protocol: 0x%x Matched with the one in Table\n", SerDesProtocol));
      break;
    }
    Config++;
  }

  if (!Config->Protocol) {
    return EFI_NOT_FOUND;
  }

  for (Count = 0; Count < SerDesNumLanes; Count++) {
    if (Config->SerDesLane[Count] != 0) {
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Get Lane protocol on provided SerDes Lane and execute callback function.

  @param  SerDes                  SerDes number.
  @param  SerDesProtocol          SerDes protocol number.
  @param  SerDesNumLanes          Number of SerDes Lanes.
  @param  SerDesMaxProtocol       Max SerDes protocol number.
  @param  Config                  SerDes Configuration.
  @param  SerDesLaneProbeCallback Pointer Callback function to be called for Lane protocol
  @param  Arg                     Pointer to Arguments to be passed to callback function.
**/
VOID
SerDesInstanceProbeLanes (
  IN  UINT32                      SerDes,
  IN  UINT32                      SerDesProtocol,
  IN  UINT8                       SerDesNumLanes,
  IN  UINT32                      SerDesMaxProtocol,
  IN  SERDES_CONFIG               *Config,
  IN  SERDES_PROBE_LANES_CALLBACK SerDesLaneProbeCallback,
  IN  VOID                        *Arg
  )
{
  INT8    Lane;
  UINT32  LaneProtocol;

  // Invoke callback for all lanes in the SerDes instance:
  for (Lane = 0; Lane < SerDesNumLanes; Lane++) {
    LaneProtocol = GetSerDesProtocol (SerDes, SerDesProtocol, Lane, SerDesMaxProtocol, Config);
    ASSERT (LaneProtocol < SerDesMaxProtocol);
    if (LaneProtocol != 0x0) {
      SerDesLaneProbeCallback (LaneProtocol, Arg);
    }
  }
}

/**
  Function to fill SerDes map information.

  @param  SerDes              SerDes number.
  @param  SerDesProtocol      SerDes protocol number.
  @param  SerDesNumLanes      Number of SerDes Lanes.
  @param  SerDesMaxProtocol   Max SerDes protocol number.
  @param  Config              SerDes Configuration.
  @param  SerDesProtocolMap   Output SerDes protocol map of enabled devices.

**/
EFI_STATUS
GetSerDesMap (
  IN  UINT32                    SerDes,
  IN  UINT32                    SerDesProtocol,
  IN  UINT8                     SerDesNumLanes,
  IN  UINT32                    SerDesMaxProtocol,
  IN  SERDES_CONFIG             *Config,
  OUT UINT64                    *SerDesProtocolMap
  )
{
  INTN                   Lane;
  EFI_STATUS             Status;
  UINT32                 LaneProtocol;

  Status = IsSerDesProtocolValid (SerDes, SerDesProtocol, SerDesNumLanes, Config);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: SERDES%d[PRTCL] = 0x%x is not valid, Status = %r \n",
            __FUNCTION__, SerDes + 1, SerDesProtocol, Status));
    return Status;
  }

  for (Lane = 0; Lane < SerDesNumLanes; Lane++) {
    LaneProtocol = GetSerDesProtocol (SerDes, SerDesProtocol, Lane, SerDesMaxProtocol, Config);
    if (LaneProtocol >= SerDesMaxProtocol) {
      DEBUG ((DEBUG_ERROR, "Unknown SerDes lane protocol %d\n", LaneProtocol));
      return EFI_NO_MAPPING;
    }
    *SerDesProtocolMap |= (BIT0 << (LaneProtocol));
  }

  return EFI_SUCCESS;
}
