#! /bin/bash -p
#
# basic script for manipulating COM-Express GPIO output pins on a Advantech SOM-5782 (Core
# Duo). SOM-5761 (Intel Atom) has the same pinout configuration.
#
# This script currently only deals with handling the COM-Express signals GPO0, GPO1, GPO2, GPO3.
#
# GPO0 is pin 0, GPO1 is pin 1, GPO2 is pin 2 and GPO3 is pin 3.
#
# e.g. Set pin 1 low -
#
#        SetGPO.sh 1 OFF
#
# On the CPU module these signals are connected to certain pins on a Fintech F75111R chip.
#
# The F75111R is connected to the PC "south bridge" via the "LPC" (Low Pin Count) interface, which
# is just an "i2c" two-wire serial interface. Hence on software side we are able to use the i2c
# communication tools from lm_sensors project to access control registers of the F75111R.
#
# On the 28-SSOP pin package, GPO0-3 are connected to pins 23-20 of the F75111R (see table below).
#
# Table of pin assignments.
#
# "CE" -> COM-Express
#  CE connector     CE signal name      F75111R pin no.     F75111R signal name.    SOM-DB5700 CN27 Pin No.
#      A93              GPO0                 23               GPIO24/LED24                   4
#      B54              GPO1                 22               GPIO25/LED25                   6
#      B57              GPO2                 21               GPIO26/LED26                   8
#      B63              GPO3                 20               GPIO27/LED27                  13
#
#      A54              GPI0                 11               GPIO11/LED11                   1
#      A63              GPI1                 12               GPIO12/LED12                   3
#      A67              GPI2                 16               GPIO16/LED16                   5
#      A85              GPI3                 17               GPIO17/LED17                   7
#
#
# SOM-DB5700 CN27 (16 Pin IDC) layout:
#
#      GPI0 * 1    2 * VCC
#      GPI1 * 3    4 * GPO0
#      GPI2 * 5    6 * GPO1
#      GPI3 * 7    8 * GPO2
#       GND * 9   10 * +12V
#        NC * 11  12 * NC
#      GPO3 * 13  14 * NC
#       GND * 15  16 * +12V
#
# On last note, F75111R docs say i2c address will be either 0x9C or 0x6E, on the SOM-5782 and
# SOM-5761 it actually appears at address 0x4e. Haven't made any attempt to investigate why.
#

# LPC bus is bus 0 on our SOM's
BUS=0

# as noted above, it shows up at i2c address 0x4E
F75111_ADDRESS=0x4E

# F75111 register addresses and mode constants


F75111__CCR=0x01
F75111___SR=0x02
F75111_CFSR=0x03

# pin mode constants for PMFS1 and PMFS2
PMFS_GPIO=0
PMFS_LED=1

# pin mode function select registers - bits are assigned slightly weird
# 0x04 - bit 7 -> GPIO27/LED27
#        bit 6 -> GPIO15/LED15
#        bit 5 -> GPIO14/LED14
#        bit 4 -> GPIO22/LED22
#        bit 3 -> GPIO21/LED21
#        bit 2 -> GPIO20/LED20
#        bit 1 -> GPIO17/LED17
#        bit 0 -> GPIO16/LED16
#
# 0x05 - bit 7 -> reserved
#        bit 6 -> reserved
#        bit 5 -> reserved
#        bit 4 -> reserved
#        bit 3 -> GPIO23/LED23
#        bit 2 -> GPIO24/LED24
#        bit 1 -> GPIO25/LED25
#        bit 0 -> GPIO26/LED26
#
# Mapping from COM-Express signal name to mode function reg bit:
#        GPO0 0x05:2
#        GPO1 0x05:1
#        GPO2 0x05:0
#        GPO3 0x04:7

F75111_PMFS1=0x04
F75111_PMFS2=0x05

# rest of bit assignments are relatively straight forward.

# LED freq constants for LFCRxx when corresponding pin mode is LED
LFCR_TRI=0x00
LFCR_0_5HZ=0x00
LFCR_1_0HZ=0x01
LFCR_LOW=0x03

F75111_LFCR1l=0x06
F75111_LFCR1h=0x07
F75111_LFCR2l=0x08
F75111_LFCR2h=0x09
# our outputs would all be controlled by LFCR2h if LED mode is selected in corresponding bit in PMFSx.

# Output Control Reg mode flags.
OC_INPUT=0
OC_OUTPUT=1

# Level/Pulse control mode flags.
LP_LVL=0
LP_PULSE=1

# pulse width mode constants
PW_500us=0x00
PW_001ms=0x01
PW_020ms=0x02
PW_100ms=0x03

# pull-up resistor mode flags.
PU_ON=1
PU_OFF=0

# input debounce mode flags
IDB_ON=1
IDB_OFF=0

# pulse inverse control flags
PULSE_LOW=0
PULSE_HIGH=1

# Edge detect enable flag values.
ED_ENABLE=1
ED_DISABLE=0

# IRQ/SMI control flag values.
IRQ_ON=1
IRQ_OFF=0

# output drive enable flag values
HI_BUFF_ON=1
HI_BUFF_OFF=0

# input debounce time flag values.
IDB_07us=0
IDB_25ms=1


F75111_OCR1=0x10
F75111_ODR1=0x11
F75111_ISR1=0x12
F75111_LPC1=0x13
F75111_PWC1=0x14
F75111_PUC1=0x15
F75111_IDR1=0x16
F75111_PIR1=0x17
F75111_EDE1=0x18
F75111_EDS1=0x19
F75111_IRE1=0x1A
F75111_ODE1=0x1B
F75111_IDB1=0x1C

F75111_OCR2=0x20
F75111_ODR2=0x21
F75111_ISR2=0x22
F75111_LPC2=0x23
F75111_PWC2=0x24
F75111_PUC2=0x25
F75111_IDR2=0x26
F75111_PIR2=0x27
F75111_EDE2=0x28
F75111_EDS2=0x29
F75111_IRE2=0x2A
F75111_ODE2=0x2B
F75111_IDB2=0x2C

F75111_WDCR=0x34
F75111_WDSR=0x35
F75111_WDPC=0x36
F75111_WDTR=0x37

F75111_OCR3=0x40
F75111_ODR3=0x41
F75111_ISR3=0x42
F75111_LPC3=0x43
F75111_PWC3=0x44

F75111_IDR3=0x46
F75111_PIR3=0x47
F75111_EDE3=0x48
F75111_EDS3=0x49
F75111_IRE3=0x4A

F75111_IDB3=0x4C


F75111_CHIPID1=0x5A
F75111_CHIPID2=0x5B
F75111_VERSION=0x5C
F75111_VENDOR1=0x5D
F75111_VENDOR2=0x5E

F75111_CHIPID1_VALUE=0x03
F75111_CHIPID2_VALUE=0x00
F75111_VERSION_VALUE=0x10
F75111_VENDOR1_VALUE=0x19
F75111_VENDOR2_VALUE=0x34

I2C_DEV_NODE=/dev/i2c-0

which i2cset > /dev/null
if [ $? -ne 0 ]; then
  echo "This script requires the i2c-tools package !";
  exit 1;
fi

# check if i2c-i801 chip module is loaded, and load it if not
if [ $( lsmod | grep i2c_i801 | wc -c ) -eq 0 ]; then
    modprobe i2c-i801
fi;

# check if i2c-dev module is loaded, and load it if not
if [ $( lsmod | grep i2c_dev | wc -c ) -eq 0 ]; then
    modprobe i2c-dev
fi;

# delay for a period to allow the device node to be created
WAIT_COUNT="5"
while [ $WAIT_COUNT -gt 0 ]; do
    if [ -e $I2C_DEV_NODE ]; then
        WAIT_COUNT="0";
    else
        WAIT_COUNT=$(($WAIT_COUNT - 1));
        sleep 1;
    fi
done

# check if we have write privaleges, and abort if not
# actually, all we care is if we can access /dev/i2c-0
if [ ! -w $I2C_DEV_NODE ]; then
  echo "Can't write to $I2C_DEV_NODE - check permissions or use sudo"
  exit 1;
fi

# $1 reg address.
# $2 value
# $3 mask
setReg ()
{
    i2cset -y $BUS $F75111_ADDRESS $1 $2 b $3 > /dev/null
}

SetOutputsToLevelModeWithDriveEnable ()
{
    if [ ! -f /tmp/GPIO_SETUP_DONE ]; then
        touch /tmp/GPIO_SETUP_DONE
        # explicitly set data register to set GPO 0 high prior to
        # configuring outputs to ensure that ~ResetDSP is in an "On" state.
        SetOutputN 0 on

        # set pins to GPIO mode -> GPIO27 is bit 7 PMFS1, GPIO24,25,26 in bits 0-2 of PMFS2
        setReg $F75111_PMFS1 0x00 0x80
        setReg $F75111_PMFS2 0x00 0x07

        # set pins to output
        setReg $F75111_OCR2 0xF0 0xF0

        # set level/pulse to level
        setReg $F75111_LPC2 0x00 0xF0

        # disable pull-up
        setReg $F75111_PUC2 0x00 0xF0

        # enable high drive.
        setReg $F75111_ODE2 0xF0 0xF0
    fi
}

SetOutPutNFlashModeON ()
{
    # mask=$(( 1 << ($1 + 4) ))
    mode_mask=0;
    mode_reg=0
    freq_mask=$(( 3 << ( $1 * 2 ) ))
    freq_value=$(( $2 << ( $1 * 2 ) ))

    case $1 in

    0|1|2)
        mode_mask=$(( 4 >> ( $1 ) ))
        mode_reg=$F75111_PMFS2
        ;;

    3)
        mask=128
        mode_reg=$F75111_PMFS1
        ;;

    *)
        return
        ;;

    esac

    setReg $mode_reg $mode_mask $mode_mask
    setReg $F75111_LFCR2h $freq_value $freq_mask
}

SetOutPutNFlashModeOFF ()
{
    # mask=$(( 1 << ($1 + 4) ))
    mode_mask=0;
    mode_reg=0

    case $1 in

    0|1|2)
        mode_mask=$(( 4 >> ( $1 ) ))
        mode_reg=$F75111_PMFS2
        ;;

    3)
        mask=128
        mode_reg=$F75111_PMFS1
        ;;

    *)
        return
        ;;

    esac

    setReg $mode_reg 0 $mode_mask
}


SetOutputN ()
{
    mask=$(( 1 << ($1 + 4) ))
    
    case $2 in

	on|ON|oN|On)
	    setReg $F75111_ODR2 $mask $mask;
	    ;;

	off|ofF|oFf|oFF|Off|OfF|OFf|OFF)
	    setReg $F75111_ODR2 0 $mask;
	    ;;
    esac
}

DisplaySyntax ()
{
    echo "Syntax:"
    echo -e "\tSetGPO <pin_no> \"on\"|\"off\""
    echo -e "\tSetGPO comled \"off\"|\"green\"|\"orange\"|\"red\"|\"green_1Hz\"|\"orange_1Hz\"|\"green_slow\"|\"orange_slow\""
    echo -e "\tSetGPO reset dsp"
    echo ""
    echo "<pin_no> is 0-3 for direct line control of GPO pins"
    echo ""
    echo "comled sets GPO pins 1 and 2 for edrivex COM status bicolour LED"
    echo ""
    echo "reset dsp toggles GPO pin 0 off and then on to reset the DSP"
    echo ""
}


if [ $# -ne 2 ]; then
  DisplaySyntax;
  exit 1;
fi;

SetOutputsToLevelModeWithDriveEnable

shopt -s nocasematch

case $1 in

    0|1|2|3)
        SetOutputN $1 $2;
        ;;

    comled)
        # from AGS-232 LED1/LED2 : L/H=green H/L=yellow L/L=off
        # some prototypes had a three terminal LED where H/H gave a third colour we call "red"
        # Now we have AGS-974 "bi-colour" LED operation has changed where green and yellow/orange have swapped so
        # L/H=orange H/L=green L/L=off
        case $2 in
          orange)
            SetOutPutNFlashModeOFF 1;
            SetOutPutNFlashModeOFF 2;
            SetOutputN 1 OFF;
            SetOutputN 2 ON;
            ;;

          green)
            SetOutPutNFlashModeOFF 1;
            SetOutPutNFlashModeOFF 2;
            SetOutputN 1 ON;
            SetOutputN 2 OFF;
            ;;

          red)
            SetOutPutNFlashModeOFF 1;
            SetOutPutNFlashModeOFF 2;
            SetOutputN 1 ON;
            SetOutputN 2 ON;
            ;;

          off)
            SetOutPutNFlashModeOFF 1;
            SetOutPutNFlashModeOFF 2;
            SetOutputN 1 OFF;
            SetOutputN 2 OFF;
            ;;

          orange_1Hz)
            SetOutPutNFlashModeOFF 1;
            SetOutputN 1 OFF;
            SetOutPutNFlashModeON 2 2;
            ;;

          green_1Hz)
            SetOutPutNFlashModeOFF 2;
            SetOutputN 2 OFF;
            SetOutPutNFlashModeON 1 2;
            ;;

          orange_slow)
            SetOutPutNFlashModeOFF 1;
            SetOutputN 1 OFF;
            SetOutPutNFlashModeON 2 1;
            ;;

          green_slow)
            SetOutPutNFlashModeOFF 2;
            SetOutputN 2 OFF;
            SetOutPutNFlashModeON 1 1;
            ;;

        esac
        ;;

    reset)
        case $2 in
            dsp)
                SetOutputN 0 off;
                SetOutputN 0 on;
                ;;

            *)
                echo "bad parameter: $2";
                DisplaySyntax;
                ;;
        esac
    ;;

    *)
      echo "bad parameter: $1";
      DisplaySyntax;
      ;;
esac
