#!/bin/bash
#
#
#    Copyright (c) 2008 Brocade Communications, Inc.
#    All rights reserved.
#
#
#    Description:
#      serdes_show [n] - show current SERDES parameters for port(s) n,
#      on GoldenEye2 ASIC, using information from the portregshow command
#
#
# GoldenEye2 has a large set of registers per port. The SERDES register set
# consists of three similar subsets-- one for full data rate (8G), another
# for half data rate (4G), and another for quarter data rate (2G and 1G).
# The following variables will represent these register values. The naming
# convention is _F = full, _H = half, and _Q = quarter.
Ctrl0_F=0; Ctrl1_F=0; Ctrl2_F=0; Ctrl3_F=0; DFELTH_F=0; TLTH_F=0; ZCNT_F=0; Z1CNT_F=0
Ctrl0_H=0; Ctrl1_H=0; Ctrl2_H=0; Ctrl3_H=0; DFELTH_H=0; TLTH_H=0; ZCNT_H=0; Z1CNT_H=0
Ctrl0_Q=0; Ctrl1_Q=0; Ctrl2_Q=0; Ctrl3_Q=0; DFELTH_Q=0; TLTH_Q=0; ZCNT_Q=0; Z1CNT_Q=0

function parse_regs {
 # Given a list of pairs such that $1 is a register name and $2 is the
 # corresponding register value, assign the register values to appropriate
 # shell variables.
 while [ $# -gt 1 ]
 do
  case "$1" in
   EPB_TXFDR_CTL0_ADDR)     Ctrl0_F=$2 ;;
   EPB_TXFDR_CTL1_ADDR)     Ctrl1_F=$2 ;;
   EPB_TXFDR_CTL2_ADDR)     Ctrl2_F=$2 ;;
   EPB_TXFDR_CTL3_ADDR)     Ctrl3_F=$2 ;;
   EPB_SMCTL_DFELTH_ADDR)   DFELTH_F=$2 ;;
   EPB_SMCTL_TLTH_ADDR)     TLTH_F=$2 ;;
   EPB_SMCTL_ZCNT_ADDR)     ZCNT_F=$2 ;;
   EPB_SMCTL_Z1CNT_ADDR)    Z1CNT_F=$2 ;;
   EPB_TXFDR_CTL0_ADDR_H)   Ctrl0_H=$2 ;;
   EPB_TXFDR_CTL1_ADDR_H)   Ctrl1_H=$2 ;;
   EPB_TXFDR_CTL2_ADDR_H)   Ctrl2_H=$2 ;;
   EPB_TXFDR_CTL3_ADDR_H)   Ctrl3_H=$2 ;;
   EPB_SMCTL_DFELTH_ADDR_H) DFELTH_H=$2 ;;
   EPB_SMCTL_TLTH_ADDR_H)   TLTH_H=$2 ;;
   EPB_SMCTL_ZCNT_ADDR_H)   ZCNT_H=$2 ;;
   EPB_SMCTL_Z1CNT_ADDR_H)  Z1CNT_H=$2 ;;
   EPB_TXFDR_CTL0_ADDR_Q)   Ctrl0_Q=$2 ;;
   EPB_TXFDR_CTL1_ADDR_Q)   Ctrl1_Q=$2 ;;
   EPB_TXFDR_CTL2_ADDR_Q)   Ctrl2_Q=$2 ;;
   EPB_TXFDR_CTL3_ADDR_Q)   Ctrl3_Q=$2 ;;
   EPB_SMCTL_DFELTH_ADDR_Q) DFELTH_Q=$2 ;;
   EPB_SMCTL_TLTH_ADDR_Q)   TLTH_Q=$2 ;;
   EPB_SMCTL_ZCNT_ADDR_Q)   ZCNT_Q=$2 ;;
   EPB_SMCTL_Z1CNT_ADDR_Q)  Z1CNT_Q=$2 ;;
  esac
  shift; shift
 done
}

function show_subset {
 # show one data rate's set of SERDES values.
 # References: SDM8G90 SerDes Preliminary Data Sheet, especially pages 138 and
 # 142. Also, compare to the ASIC driver code in c2.h. For example, look at
 # the macros EPB_TXFDR_CTL0_VAL(amp), EPB_TXFDR_CTL2_VAL(ipre, ipst) and
 # similar macros in that file.
 declare -i Ctrl0=0x$1
 declare -i Ctrl1=0x$2
 declare -i Ctrl2=0x$3
 declare -i Ctrl3=0x$4
 declare -i DFELTH=0x$5
 declare -i TLTH=0x$6
 declare -i ZCNT=0x$7
 declare -i Z1CNT=0x$8
 let "amp=($Ctrl0 >> 1) & 63"
 let "slew=$Ctrl1 & 3"
 let "pre=(($Ctrl2 >> 6) & 3) | (($Ctrl3 & 3) << 2)"
 let "pst=($Ctrl2 >> 1) & 15"
 let "main=($Ctrl3 >> 3) & 31"
 let "dfe=$DFELTH & 63"
 let "tlth=$TLTH & 63"
 let "zcnt=$ZCNT & 31"
 let "z1cnt=$Z1CNT & 31"
 printf "%2d   %2d   %2d   %2d   %2d   %2d   %2d   %2d   %2d"\
         $amp  $slew $pre  $pst  $main $dfe  $tlth $zcnt $z1cnt
}

function show_port {
 # Show one port's SERDES settings.
 # First, dump all the registers using portregshow, grep for the register
 # names we want, and throw out some extraneous junk. That leaves us with
 # a bunch of pairs of register name and register value. Pass that list through
 # the parse_regs function to produce a bunch of register values. GoldenEye2
 # has three subsets of similar registers per port, one for full data rate,
 # one for half data rate, and one for quarter data rate. Print the values for
 # each subset by calling the show_subset function.
 printf "%2d   " $1
 parse_regs `portregshow $1 |\
 grep -e EPB_TXFDR_ -e EPB_SMCTL_ |\
 sed 's/^[^ ]*: //' | sed 's/[^ 	]*: //'`
 show_subset $Ctrl0_F $Ctrl1_F $Ctrl2_F $Ctrl3_F $DFELTH_F $TLTH_F $ZCNT_F $Z1CNT_F
 printf "        "
 show_subset $Ctrl0_H $Ctrl1_H $Ctrl2_H $Ctrl3_H $DFELTH_H $TLTH_H $ZCNT_H $Z1CNT_H
 printf "        "
 show_subset $Ctrl0_Q $Ctrl1_Q $Ctrl2_Q $Ctrl3_Q $DFELTH_Q $TLTH_Q $ZCNT_Q $Z1CNT_Q
 printf "\n"
}

function last_port_number_from_switchshow {
 # extract last port number from the last line of switchshow
 # this assumes a pizza box -- on a modular system the port # is the 3rd param
 N=$2
}

# print the heading
printf "port amp  slew pre  pst  main dfe  tlth zcnt z1cnt"
printf " 1/2 amp  slew pre  pst  main dfe  tlth zcnt z1cnt"
printf " 1/4 amp  slew pre  pst  main dfe  tlth zcnt z1cnt\n"

# If no command line arguments, do all ports
if [ $# = 0 ]
then
 last_port_number_from_switchshow `switchshow | tail -1`
 for ((i=0; i<=$N; i++))
 do
  show_port $i
 done
else
 for i in $*
 do
  show_port $i
 done
fi
