#!/bin/bash
#
# Copyright (C) 2010-2017, Mellanox Technologies Ltd.  ALL RIGHTS RESERVED.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
#

### BEGIN INIT INFO
# Provides:        bsp for Mellanox systems
# Required-Start:  $syslog 
# Required-Stop:   $syslog
# Default-Start:   2 3 4 5       
# Default-Stop:    0 1 6
# Short-Description: Mellanox x86 MSN systems bsp
# Description:       Mellanox system support
# Supported systems:
#  MSN274*		Panther SF
#  MSN21*		Bulldog
#  MSN24*		Spider
#  MSN27*|MSB*|MSX*	Neptune, Tarantula, Scorpion, Scorpion2
#  MSN201*		Boxer
#  QMB7*|SN37*|SN34*	Jupiter, Jaguar, Anaconda
# Available options:
# start        - install all BSP kernel drivers, connect drivers to devices, create BSP dictionary as symbolic
#                links to sysfs entries
# stop         - destroy BSP dictionary, disconnect drivers from devices, uninstall BSP kernel drivers
# restart      - combined stop and start sequence
#                driver reloading
### END INIT INFO

. /lib/lsb/init-functions

module_load_path=(	i2c/i2c-dev.ko \
			hwmon/pmbus/pmbus.ko \
			hwmon/pmbus/tps53679.ko \
			iio/adc/max1363.ko \
			hwmon/coretemp.ko \
			hwmon/lm75.ko \
			hwmon/tmp102.ko \
			misc/eeprom/at24.ko \
			leds/trigger/ledtrig-timer.ko
			net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko \
			i2c/busses/i2c-mlxcpld.ko \
			platform/x86/mlx-platform.ko)

module_unload_list=(	at24 tmp102 lm75 coretemp max1363 tps53679 pmbus \	
			mlxsw_minimal mlxsw_i2c mlxsw_core mlx_platform \
			mlxreg_io leds_mlxreg mlxreg_hotplug i2c_mlxcpld \
			ledtrig_timer i2c_dev)

msn2700_connect_table=(	mlxsw_minimal 0x48 2 \
			pmbus 0x27 5 \
			pmbus 0x41 5 \
			max11603 0x6d 5 \
			lm75 0x4a 7 \
			24c32 0x51 8 \
			max11603 0x6d 15 \
			24c32 0x51 16 \
			lm75 0x49 17)

msn2700_dis_table=(	0x27 5 \
			0x41 5 \
			0x6d 5 \
			0x4a 7 \
			0x51 8 \
			0x6d 15 \
			0x51 16 \
			0x49 17 \
			0x48 2)

msn2100_connect_table=(	mlxsw_minimal 0x48 2 \
			pmbus 0x27 5 \
			pmbus 0x41 5 \
			max11603 0x6d 5 \
			lm75 0x4a 7 \
			lm75 0x4b 7 \
			24c32 0x51 8 \
			max11603 0x6d 15 \
			24c32 0x51 16)

msn2100_dis_table=(	0x27 5 \
			0x41 5 \
			0x6d 5 \
			0x4a 7 \
			0x4b 7 \
			0x51 8 \
			0x6d 15 \
			0x51 16 \
			0x48 2)

msn2740_connect_table=(	mlxsw_minimal 0x48 2 \
			pmbus 0x27 5 \
			pmbus 0x41 5 \
			max11603 0x64 5 \
			tmp102 0x49 6 \
			tmp102 0x48 7 \
                        24c32 0x51 8 \
			max11603 0x6d 15 \
			24c32 0x51 16)

msn2740_dis_table=(	0x27 5 \
			0x41 5 \
			0x64 5 \
			0x49 6 \
			0x48 7 \
			0x51 8 \
			0x6d 15 \
			0x51 16 \
			0x48 2)

msn2010_connect_table=(	mlxsw_minimal 0x48 2 \
			max11603 0x6d 5 \
			tps53679 0x70 5 \
			tps53679 0x71 5 \
			lm75 0x4a 7 \
			lm75 0x4b 7 \
			24c32 0x51 8 \
			max11603 0x6d 15 \
			24c32 0x51 16)

msn2010_dis_table=(	0x71 5 \
			0x70 5 \
			0x6d 5 \
			0x4b 7 \
			0x4a 7 \
			0x51 8 \
			0x6d 15 \
			0x51 16 \
			0x48 2)

qmb700_connect_table=(	mlxsw_minimal 0x48 2 \
			max11603 0x64 5 \
			tps53679 0x70 5 \
			tps53679 0x71 5 \
			tmp102 0x49 7 \
			tmp102 0x4a 7 \
			24c32 0x51 8 \
			max11603 0x6d 15 \
			24c32 0x51 16)

qmb700_dis_table=(	0x64 5 \
			0x70 5 \
			0x71 5 \
			0x49 7 \
			0x4a 7 \
			0x51 8 \
			0x6d 15 \
			0x51 16 \	
			0x48 2)

fan_max_speed=23000
fan_min_speed=10500

ACTION=$1

is_module()
{
        /sbin/lsmod | grep -w "$1" > /dev/null
        RC=$?
        return $RC
}

msn274x_specific()
{
	connect_size=${#msn2740_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${msn2740_connect_table[i]}
	done
	disconnect_size=${#msn2740_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${msn2740_dis_table[i]}
	done
	echo 5 > /bsp/config/fan_inversed
	echo 2 > /bsp/config/cpld_num
}

msn21xx_specific()
{
	connect_size=${#msn2100_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${msn2100_connect_table[i]}
	done
	disconnect_size=${#msn2100_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${msn2100_dis_table[i]}
	done
	echo 5 > /bsp/config/fan_inversed
	echo 2 > /bsp/config/cpld_num
}

msn24xx_specific()
{
	connect_size=${#msn2700_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${msn2700_connect_table[i]}
	done
	disconnect_size=${#msn2700_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${msn2700_dis_table[i]}
	done
	echo 9 > /bsp/config/fan_inversed
	echo 3 > /bsp/config/cpld_num
}

msn27xx_msb_msx_specific()
{
	connect_size=${#msn2700_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${msn2700_connect_table[i]}
	done
	disconnect_size=${#msn2700_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${msn2700_dis_table[i]}
	done
	echo 9 > /bsp/config/fan_inversed
	echo 3 > /bsp/config/cpld_num
}

msn201x_specific()
{
	connect_size=${#msn2010_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${msn2010_connect_table[i]}
	done
	disconnect_size=${#msn2010_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${msn2010_dis_table[i]}
	done
	echo 5 > /bsp/config/fan_inversed
	echo 2 > /bsp/config/cpld_num
}

qmb7xxx_sn37x_sn34x_specific()
{
	connect_size=${#qmb700_connect_table[@]}
	for ((i=0; i<$connect_size; i++))
	do
		connect_table[i]=${qmb700_connect_table[i]}
	done
	disconnect_size=${#qmb700_dis_table[@]}
	for ((i=0; i<$disconnect_size; i++))
	do
		dis_table[i]=${qmb700_dis_table[i]}
	done
	echo 3 > /bsp/config/cpld_num
}

check_system()
{
	manufacturer=`cat /sys/devices/virtual/dmi/id/sys_vendor | awk '{print $1}'`
	if [ "$manufacturer" != "Mellanox" ]; then
		echo $manufacturer is not Mellanox 
		exit 0
	fi

	product=`cat /sys/devices/virtual/dmi/id/product_name`

	case $product in
		MSN274*)
			msn274x_specific
			;;
		MSN21*)
			msn21xx_specific
			;;
		MSN24*)
			msn24xx_specific
			;;
		MSN27*|MSB*|MSX*)
			msn27xx_msb_msx_specific
			;;
		MSN201*)
			msn201x_specific
			;;
		QMB7*|SN37*|SN34*)
			qmb7xxx_sn37x_sn34x_specific
			;;
		*)
			echo $product is not supported 
			exit 0
			;;
	esac

	kernel_release=`uname -r`
}

load_module()
{
	filename=`basename /lib/modules/${kernel_release}/kernel/drivers/$1`
	name="${filename%.*}"
	alternative=`echo "$name" | tr '-' '_'`
	if [ -f /lib/modules/${kernel_release}/kernel/drivers/$1 ]; then
		if is_module $name || is_module $alternative; then
			log_warning_msg "$name module has already been loaded"
		else
			param=
			if [ $name = "at24" ]; then
				param="io_limit=32"
			fi
			if modprobe $name $param 2>/dev/null; then
				log_success_msg "$name module load passed"
			else
				log_failure_msg "$name module load failed"
			fi
		fi
	fi

	return 0
}

unload_module()
{
	if [ -d /sys/module/$1 ]; then
		if [ ! -f /sys/module/$1/refcnt ]; then
			return 0
		fi
		refcnt=`cat /sys/module/$1/refcnt`
		if [ "$refcnt" -gt 0 ]; then
			return 0
		fi

		if is_module $1; then
			if rmmod $1 2>/dev/null ; then
				log_success_msg "$1 module is unloaded"
			else
				log_failure_msg "$1 module unload failed"
			fi
		else
			log_warning_msg "No $1 module loaded"
		fi
	fi

	return 0
}

connect_device()
{
	if [ -f /sys/bus/i2c/devices/i2c-$3/new_device ]; then
		addr=`echo $2 | tail -c +3`
		if [ ! -d /sys/bus/i2c/devices/$3-00$addr ] &&
		   [ ! -d /sys/bus/i2c/devices/$3-000$addr ]; then
			echo $1 $2 > /sys/bus/i2c/devices/i2c-$3/new_device
		fi
	fi

	return 0
}

disconnect_device()
{
	if [ -f /sys/bus/i2c/devices/i2c-$2/delete_device ]; then
		addr=`echo $1 | tail -c +3`
		if [ -d /sys/bus/i2c/devices/$2-00$addr ] ||
		   [ -d /sys/bus/i2c/devices/$2-000$addr ]; then
			echo $1 > /sys/bus/i2c/devices/i2c-$2/delete_device
		fi
	fi

	return 0
}

load_modules()
{
	log_daemon_msg "Starting Mellanox x86 system bsp modules"
	log_end_msg 0

	COUNT=${#module_load_path[@]}
	for ((i=0; i<$COUNT; i++))
	do
		load_module ${module_load_path[i]}
	done

	return 0
}

unload_modules()
{
	log_daemon_msg "Stopping Mellanox x86 system bsp module"
	log_end_msg 0

	COUNT=${#module_unload_list[@]}
	for ((i=0; i<$COUNT; i++))
	do
		unload_module ${module_unload_list[i]}
	done

	return 0
}

connect_platform()
{
	for ((i=0; i<$connect_size; i+=3))
	do
		connect_device 	${connect_table[i]} ${connect_table[i+1]} \
				${connect_table[i+2]}
        done
}

disconnect_platform()
{
	for ((i=0; i<$disconnect_size; i+=2))
	do
		disconnect_device ${dis_table[i]} ${dis_table[i+1]}
	done
}

status()
{
    lsmod
}

case $ACTION in
        start)
		if [ ! -d /bsp ]; then
			mkdir /bsp
		fi
		if [ ! -d /bsp/config ]; then
			mkdir -p /bsp/config
		fi
		check_system
		depmod -a 2>/dev/null
		load_modules
		sleep 1
		echo $fan_max_speed > /bsp/config/fan_max_speed
		echo $fan_min_speed > /bsp/config/fan_min_speed
		if [ ! -d /bsp/cpld ]; then
			mkdir /bsp/cpld
		fi
		if [ ! -h /bsp/cpld/cpld_version ]; then
			ln -sf /usr/bin/mellanox-system-helper.sh /bsp/cpld/cpld_version
		fi
		/bsp/cpld/cpld_version port > /bsp/cpld/cpld_port_version
		if [ ! -d /bsp/system ]; then
			mkdir /bsp/system
		fi
		if [ ! -d /bsp/power ]; then
			mkdir -p /bsp/power
		fi
		if [ ! -h /bsp/power/pwr_consum ]; then
			ln -sf /usr/bin/mellanox-system-helper.sh /bsp/power/pwr_consum
		fi
		if [ ! -h /bsp/power/pwr_sys ]; then
			ln -sf /usr/bin/mellanox-system-helper.sh /bsp/power/pwr_sys
		fi
		connect_platform
	;;
        stop)
		check_system
		disconnect_platform
		unload_modules
		# Clean bsp directory - remove folder if it's empty
		if [ -d /bsp ]; then
			sleep 3
			for filename in /bsp/*; do
				if [ -d $filename ]; then
					if [ -z "$(ls -A $filename)" ]; then
						rm -rf $filename
					fi
				elif [ -L $filename ]; then
					unlink $filename
				fi
			done
			rm -rf /bsp/config
			if [ -z "$(ls -A /bsp)" ]; then
				rm -rf /bsp
			fi
		fi
	;;
        restart)
		$0 stop
		$0 start
	;;
	status)
		status
	;;
	*)
		echo
		echo "Usage: `basename $0` {start|stop|restart}"
		echo
		exit 1
	;;
esac
