【Note9】

【Note9】_第1张图片

#!/bin/bash
#
# Copyright 2019-present Huaqin. All Rights Reserved.
#
# This program file 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; version 2 of the License.
#
# 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.
#
#

PART1="/dev/mmcblk0p1"
PART2="/dev/mmcblk0p2"
PART1_MOUNT_POINT="/overlay/rw"
PART2_MOUNT_POINT="/var/volatile/log/"
DEV="/dev/mmcblk0"

prog=$(basename "$0")
usage() {
    echo "Usage: ${prog} "
    echo
    echo "Note:"
    echo "  configuration : System configurations and user data"
    echo "  all           : All disk data,include user data, syslog and system configurations"
    echo
    echo "Examples:"
    echo "  $prog configuration "
    echo "  $prog all "
    echo
    exit 1
}

check_parameter() {
    if [ $# -ne 1 ]; then
        usage
    fi

    obj="$1"
    if [ "${obj}" != "configuration" ] && [ "${obj}" != "all" ]; then
        usage
    fi
}

format_fs_1() {
    mkfs.ext4 $PART1 << EOF
y
EOF
}

partition_rm_1() {
    fdisk $DEV << EOF
d
1
w
EOF
}

format_fs_2() {
    mkfs.ext4 $PART2 << EOF
y
EOF
}

partition_rm_2() {
    fdisk $DEV << EOF
d
2
w
EOF
}

format_emmc() {
    if [ "$format_flag" = 1 ]; then
        case $obj in
            configuration)
                umount -lf $PART1_MOUNT_POINT  || (echo "umount $PART1 fail !" && exit 1)
                format_fs_1
                sleep 3
                partition_rm_1
                sleep 3
                partprobe $DEV
                ;;
            all)
                umount -lf $PART2_MOUNT_POINT  || (echo "umount $PART2 fail !" && exit 1)
                format_fs_2
                sleep 3
                partition_rm_2
                sleep 3
                partprobe $DEV
                ;;
            *)
                usage
                ;;
        esac
    fi

    echo "reboot after 10S"
    sleep 10
    reboot
}

confirm_prompt() {
    case $obj in
        configuration)
            read -r -p $'This operaion will restore system configurations.\x0aPlease confirm. [Y/N] ' input
            ;;
        all)
            read -r -p $'This operaion will format emmc and discard all system configurations.\x0aPlease confirm. [Y/N] ' input
            ;;
        *)
            usage
            ;;
    esac

    case $input in
        [yY][eE][sS] | [yY])
            echo "Start to perform restore default command."
            format_flag=1
            ;;

        [nN][oO] | [nN])
            echo "Abort execution and exit."
            exit 1
            ;;

        *)
            echo "Invalid input..."
            exit 1
            ;;
    esac
}

check_parameter "$@"
confirm_prompt
format_emmc

#!/bin/bash
#
# Copyright 2019-present Huaqin. All Rights Reserved.
#
# This program file 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; version 2 of the License.
#
# 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 in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#
#shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

BLACKBOX_ADDRESS=0xdc
BLACKBOX_PAGE_ADDRESS=0xdb
OUTPUTFILE=/var/log/dump_acbel_psu_blackbox.txt

program=$(basename "$0")
usage() {
    echo "Usage:"
    echo "  $program -i "
    echo
    echo "PSU index:"
    echo "  1-4"
    echo
    echo "Examples:"
    echo "  $program -i 4"
    exit 1
}

decompose_iic_addr()
{
    bus=$(echo "$1" | awk -F "-" '{print $1}')
    str=$(echo "$1" | awk -F "-" '{print $2}')
    address=${str/00/0x}
}

check_parameter()
{
    if [ "$#" -ne 2 ];then
        usage
    fi
    device="$2"
    if [ "$device" != "1" ] && [ "$device" != "2" ] && [ "$device" != "3" ] && [ "$device" != "4" ];then
        usage
    fi
}

linear11_convert()
{
    if [ $((($1 >> 10) & 0x1)) == 1 ] ; then
        mantissa=$(($1 & 0x3FF))
        mantissa=$(($mantissa ^ 0x3ff))
        mantissa=$((~$mantissa))
    else
        mantissa=$(((((($1 & 0x7ff)) << 5)) >> 5))
    fi

    if [ $((($1 >> 15) & 0x1)) == 1 ] ; then
        exponent=$(((($1 & 0x7FFF)) >> 11))
        exponent=$(($exponent ^ 0xf))
        exponent=$((~$exponent))
    else
        exponent=$(($1>> 11))
    fi

    val_x=$((mantissa * 1000))
    if [ $exponent -ge 0 ]; then
        val_x=$(($val_x<<$exponent))
    else
        val_x=$(($val_x>>$((-$exponent))))
    fi
    echo $val_x
}

linear11_convert $1

linear11_convert()
{
    mantissa=$(((((($1 & 0x7ff)) << 5)) >> 5))
    if [ $((($1 >> 15) & 0x1)) == 1 ] ; then
        exponent=$(((($1 & 0x7FFF)) >> 11))
        exponent=$(($exponent ^ 0xf))
        exponent=$((~$exponent))
    else
        exponent=$(($1>> 11))
    fi
    val_x=$((mantissa * 1000))
    if [ $exponent -ge 0 ]; then
        val_x=$(($val_x<<$exponent))
    else
        val_x=$(($val_x>>$((-$exponent))))
    fi
    echo $val_x
}

linear16_convert()
{
    mantissa=$(printf %d "$1")
    if [ $((($2 >> 4) & 0x1)) == 1 ] ; then
        exponent=$(((((($2 << 11)) >> 11)) & 0xf))
        exponent=$(($exponent ^ 0xf))
        exponent=$((~$exponent))
    else
        exponent=$(printf %d "$2")
    fi
    val_x=$((mantissa * 1000))
    if [ $exponent -ge 0 ]; then
        val_x=$(($val_x<<$exponent))
    else
        val_x=$(($val_x>>$((-$exponent))))
    fi
    echo $val_x
}

do_action()
{
    value=$(i2ctransfer -f -y $bus w1@$address $BLACKBOX_ADDRESS r52)
    present_psu_on_time_l=${value:7:2}
    present_psu_on_time_m=${value:12:2}
    present_psu_on_time_h=${value:17:2}
    present_psu_on_time="0x""$present_psu_on_time_h""$present_psu_on_time_m""$present_psu_on_time_l"
    present_psu_on_time=$(printf %d "$present_psu_on_time")
    echo "Present total PSU ON time:                      $present_psu_on_time min" >> $OUTPUTFILE

    present_num_ac_cycles_l=${value:22:2}
    present_num_ac_cycles_h=${value:27:2}
    present_num_ac_cycles="0x""$present_num_ac_cycles_h""$present_num_ac_cycles_l"
    present_num_ac_cycles=$(printf %d "$present_num_ac_cycles")
    echo "Present number of AC power cycles:              $present_num_ac_cycles" >> $OUTPUTFILE

    present_num_pson_cycles_l=${value:32:2}
    present_num_pson_cycles_h=${value:37:2}
    present_num_pson_cycles="0x""$present_num_pson_cycles_h""$present_num_pson_cycles_l"
    present_num_pson_cycles=$(printf %d "$present_num_pson_cycles")
    echo "Present number of PSON power cycles:            $present_num_pson_cycles" >> $OUTPUTFILE

    power_on_time_l=${value:42:2}
    power_on_time_m=${value:47:2}
    power_on_time_h=${value:52:2}
    power_on_time="0x""$power_on_time_h""$power_on_time_m""$power_on_time_l"
    power_on_time=$(printf %d "$power_on_time")
    echo "Power supply total power on time:               $power_on_time" >> $OUTPUTFILE

    clock_data_l=${value:57:2}
    clock_data_m1=${value:62:2}
    clock_data_m2=${value:67:2}
    clock_data_h=${value:72:2}
    clock_data="0x""$clock_data_h""$clock_data_m2""$clock_data_m1""$clock_data_l"
    clock_data=$(printf %d "$clock_data")
    echo "Real Time Clock Data from System:               $clock_data" >> $OUTPUTFILE

    num_ac_cycles_l=${value:77:2}
    num_ac_cycles_h=${value:82:2}
    num_ac_cycles="0x""$num_ac_cycles_h""$num_ac_cycles_l"
    num_ac_cycles=$(printf %d "$num_ac_cycles")
    echo "Number of AC power cycles:                      $num_ac_cycles" >> $OUTPUTFILE

    num_pson_cycles_l=${value:87:2}
    num_pson_cycles_h=${value:92:2}
    num_pson_cycles="0x""$num_pson_cycles_h""$num_pson_cycles_l"
    num_pson_cycles=$(printf %d "$num_pson_cycles")
    echo "Number of PSON power cycles:                    $num_pson_cycles" >> $OUTPUTFILE

    status_word_l=${value:97:2}
    status_word_h=${value:102:2}
    status_word="0x""$status_word_h""$status_word_l"
    if [ $((($status_word >> 1) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: CML:                           A communications, memory or logic fault has occurred         Refer to STATUS_CML"  >> $OUTPUTFILE
    elif [ $((($status_word >> 2) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: TEMPERATURE:                   A temperature fault or warning has occurred                  Refer to STATUS_TEMPER ATURE" >> $OUTPUTFILE
    elif [ $((($status_word >> 3) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: VIN_UV_FAULT:                  An input under voltage fault has occurred                    Refer to STATUS_INPUT" >> $OUTPUTFILE
    elif [ $((($status_word >> 4) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: IOUT_OC_FAULT:                 An output over current fault has occurred                    Refer to STATUS_IOUT" >> $OUTPUTFILE
    elif [ $((($status_word >> 5) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: VOUT_OV_FAULT:                 An output overvoltage fault has occurred                     Refer to STATUS_VOUT" >> $OUTPUTFILE
    elif [ $((($status_word >> 6) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: OFF:                           This bit is asserted if the unit is not providing power to the output  " >> $OUTPUTFILE
    elif [ $((($status_word >> 10) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: FANS:                          A fan or airflow fault or warning has occurred               Refer to STATUS_FANS" >> $OUTPUTFILE
    elif [ $((($status_word >> 11) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: POWER_GOOD:                    The POWER_GOOD signal, if present, is negated                Reflects real time state of PSU" >> $OUTPUTFILE
    elif [ $((($status_word >> 13) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: INPUT:                         An input voltage, input current, or input power fault or warning has occurred   Refer to STATUS_INPUT" >> $OUTPUTFILE
    elif [ $((($status_word >> 14) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: IOUT/POUT:                     An output current or output power fault or warning has occurred                 Refer to STATUS_IOUT"  >> $OUTPUTFILE
    elif [ $((($status_word >> 15) & 0x1)) == 1 ] ; then
        echo "STATUS_WORD: VOUT:                          An output voltage fault or warning has occurred              Refer to STATUS_VOUT"  >> $OUTPUTFILE
    fi

    status_vout_l=${value:107:2}
    status_vout="0x""$status_vout_l"
    if [ $((($status_vout >> 4) & 0x1)) == 1 ] ; then
        echo "STATUS_VOUT: VOUT_UV_FAULT:                 Output Under voltage Fault       STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): OFF"  >> $OUTPUTFILE
    elif [ $((($status_vout >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_VOUT: VOUT_OV_FAULT:                 Output Overvoltage Fault         STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): OFF"  >> $OUTPUTFILE
    fi

    status_iout_l=${value:112:2}
    status_iout="0x""$status_iout_l"
    if [ $(($status_iout & 0x1)) == 1 ] ; then
        echo "STATUS_IOUT: POUT_OP_WARNING:               Predictive failure               STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_iout >> 1) & 0x1)) == 1 ] ; then
        echo "STATUS_IOUT: POUT_OP_FAULT:                 Failure                          STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    elif [ $((($status_iout >> 5) & 0x1)) == 1 ] ; then
        echo "STATUS_IOUT: IOUT_OC_WARNING:               Predictive failure               STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_iout >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_IOUT: IOUT_OC_FAULT :                Failure                          STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): OFF">> $OUTPUTFILE
    fi

    status_input_l=${value:117:2}
    status_input="0x""$status_input_l"
    if [ $(($status_input & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: PIN_OP_WARNING:              Predictive failure                STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_input >> 1) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: IIN_OC_WARNING:              Predictive failure                STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_input >> 2) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: IIN_OC_FAULT:                Input Overcurrent Fault           STATUS bit Auto Recovery: No       PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    elif [ $((($status_input >> 3) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: Unit Off for Low Input Voltage :                               Either the input voltage has never exceeded the input turn-on threshold or if the unit did start,\
        the input voltage decreased below the turn-off threshold            STATUS bit Auto Recovery: Yes                     PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    elif [ $((($status_input >> 4) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: VIN_UV_FAULT:                Input Under voltage Fault         STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    elif [ $((($status_input >> 5) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: VIN_UV_WARNING:              Predictive failure                STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_input >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_INPUT: VIN_OV_FAULT:                Input Overvoltage Fault           STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    fi

    status_temp_l=${value:122:2}
    status_temp="0x""$status_temp_l"
    if [ $((($status_temp >> 6) & 0x1)) == 1 ] ; then
        echo "STATUS_TEMPERATURE: OT_WARNING:            Over Temperature Warning          STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_temp >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_TEMPERATURE: OT_FAULT:              Over Temperature Fault            STATUS bit Auto Recovery: Yes      PSU state when bit is asserted('1'): OFF" >> $OUTPUTFILE
    fi

    status_cml_l=${value:127:2}
    status_cml="0x""$status_cml_l"
    if [ $((($status_cml >> 5) & 0x1)) == 1 ] ; then
        echo "STATUS_CML: Packet Error Check Failed:     Packet Error Check Failed                STATUS bit Auto Recovery: No    PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_cml >> 6) & 0x1)) == 1 ] ; then
        echo "STATUS_CML: Invalid/Unsupported Data:      Invalid Or Unsupported Data Received     STATUS bit Auto Recovery: No    PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    elif [ $((($status_cml >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_CML: Invalid/Unsupported Command:   Invalid Or Unsupported Command Received  STATUS bit Auto Recovery: No    PSU state when bit is asserted('1'): ON" >> $OUTPUTFILE
    fi

    status_fan_1_2_l=${value:132:2}
    status_fan_1_2="0x""$status_fan_1_2_l"
    if [ $((($status_fan_1_2 >> 3) & 0x1)) == 1 ] ; then
        echo "STATUS_FAN_1_2: Fan 1 Speed Overridden :   Fan 1 overridden occurred                STATUS bit Auto Recovery: Yes" >> $OUTPUTFILE
    elif [ $((($status_fan_1_2 >> 5) & 0x1)) == 1 ] ; then
        echo "STATUS_FAN_1_2: Fan 1 Warning :            Fan 1 warning occurred                   STATUS bit Auto Recovery: Yes" >> $OUTPUTFILE
    elif [ $((($status_fan_1_2 >> 7) & 0x1)) == 1 ] ; then
        echo "STATUS_FAN_1_2: Fan 1 Fault :              Fan 1 fault occurred                     STATUS bit Auto Recovery: Yes" >> $OUTPUTFILE
    fi

    read_vin_l=${value:137:2}
    read_vin_h=${value:142:2}
    read_vin="0x""$read_vin_h""$read_vin_l"
    read_vin=$(linear11_convert $read_vin)
    read_vin=$(awk 'BEGIN{printf "%.2f\n",'$read_vin'/1000}')
    echo "READ_VIN:                                       $read_vin V" >> $OUTPUTFILE

    read_iin_l=${value:147:2}
    read_iin_h=${value:152:2}
    read_iin="0x""$read_iin_h""$read_iin_l"
    read_iin=$(linear11_convert $read_iin)
    read_iin=$(awk 'BEGIN{printf "%.2f\n",'$read_iin'/1000}')
    echo "READ_IIN:                                       $read_iin A" >> $OUTPUTFILE

    read_vout_l=${value:157:2}
    read_vout_h=${value:162:2}
    read_vout="0x""$read_vout_h""$read_vout_l"
    mode_vout=$(i2cget -f -y $bus $address 0x20)
    read_vout=$(linear16_convert $read_vout $mode_vout)
    read_vout=$(awk 'BEGIN{printf "%.2f\n",'$read_vout'/1000}')
    echo "READ_VOUT:                                      $read_vout V" >> $OUTPUTFILE

    read_iout_l=${value:167:2}
    read_iout_h=${value:172:2}
    read_iout="0x""$read_iout_h""$read_iout_l"
    read_iout=$(linear11_convert $read_iout)
    read_iout=$(awk 'BEGIN{printf "%.2f\n",'$read_iout'/1000}')
    echo "READ_IOUT:                                      $read_iout A" >> $OUTPUTFILE

    read_temp_1_l=${value:177:2}
    read_temp_1_h=${value:182:2}
    read_temp_1="0x""$read_temp_1_h""$read_temp_1_l"
    read_temp_1=$(linear11_convert $read_temp_1)
    read_temp_1=$(awk 'BEGIN{printf "%.2f\n",'$read_temp_1'/1000}')
    echo "READ_TEMPERATURE_1 (Ambient):                   $read_temp_1 C" >> $OUTPUTFILE

    read_temp_2_l=${value:187:2}
    read_temp_2_h=${value:192:2}
    read_temp_2="0x""$read_temp_2_h""$read_temp_2_l"
    read_temp_2=$(linear11_convert $read_temp_2)
    read_temp_2=$(awk 'BEGIN{printf "%.2f\n",'$read_temp_2'/1000}')
    echo "READ_TEMPERATURE_2 (Hot Spot):                  $read_temp_2 C" >> $OUTPUTFILE

    read_temp_3_l=${value:197:2}
    read_temp_3_h=${value:202:2}
    read_temp_3="0x""$read_temp_3_h""$read_temp_3_l"
    read_temp_3=$(linear11_convert $read_temp_3)
    read_temp_3=$(awk 'BEGIN{printf "%.2f\n",'$read_temp_3'/1000}')
    echo "READ_TEMPERATURE_3 (Primary):                   $read_temp_3 C" >> $OUTPUTFILE

    read_fan_speed_1_l=${value:207:2}
    read_fan_speed_1_h=${value:212:2}
    read_fan_speed_1="0x""$read_fan_speed_1_h""$read_fan_speed_1_l"
    read_fan_speed_1=$(linear11_convert $read_fan_speed_1)
    read_fan_speed_1=$(awk 'BEGIN{printf "%.2f\n",'$read_fan_speed_1'/1000}')
    echo "READ_FAN_SPEED_1:                               $read_fan_speed_1 RPM" >> $OUTPUTFILE

    read_pout_l=${value:217:2}
    read_pout_h=${value:222:2}
    read_pout="0x""$read_pout_h""$read_pout_l"
    read_pout=$(linear11_convert $read_pout)
    read_pout=$(awk 'BEGIN{printf "%.2f\n",'$read_pout'/1000}')
    echo "READ_POUT:                                      $read_pout W" >> $OUTPUTFILE

    read_pin_l=${value:227:2}
    read_pin_h=${value:232:2}
    read_pin="0x""$read_pin_h""$read_pin_l"
    read_pin=$(linear11_convert $read_pin)
    read_pin=$(awk 'BEGIN{printf "%.2f\n",'$read_pin'/1000}')
    echo "READ_PIN:                                       $read_pin W" >> $OUTPUTFILE

    ac_shutdown=${value:237:2}
    ac_shutdown="0x""$ac_shutdown"
    ac_shutdown_l=$(($ac_shutdown & 0xf))
    ac_shutdown_l=$(printf %d "$ac_shutdown_l")
    echo "AC shutdown due to under voltage on input:      $ac_shutdown_l" >> $OUTPUTFILE
    thermal_shutdown_h=$((($ac_shutdown >> 4) & 0xf))
    thermal_shutdown_h=$(printf %d "$thermal_shutdown_h")
    echo "Thermal shutdown:                               $thermal_shutdown_h" >> $OUTPUTFILE

    output_shutdown=${value:242:2}
    output_shutdown="0x""$output_shutdown"
    output_shutdown_l=$(($output_shutdown & 0xf))
    output_shutdown_l=$(printf %d "$output_shutdown_l")
    echo "Over current or over power shutdown on output:  $output_shutdown_l" >> $OUTPUTFILE
    general_shutdown_h=$((($output_shutdown >> 4) & 0xf))
    general_shutdown_h=$(printf %d "$general_shutdown_h")
    echo "General failure shutdown:                       $general_shutdown_h" >> $OUTPUTFILE

    fan_fail_shutdown=${value:247:2}
    fan_fail_shutdown="0x""$fan_fail_shutdown"
    fan_fail_shutdown_l=$(($fan_fail_shutdown & 0xf))
    fan_fail_shutdown_l=$(printf %d "$fan_fail_shutdown_l")
    echo "Fan failure shutdown:                           $fan_fail_shutdown_l" >> $OUTPUTFILE
    voltage_shutdown_h=$((($fan_fail_shutdown >> 4) & 0xf))
    voltage_shutdown_h=$(printf %d "$voltage_shutdown_h")
    echo "Shutdown due to over voltage on output:         $voltage_shutdown_h" >> $OUTPUTFILE

    voltage_warning_no_shutdown=${value:252:2}
    voltage_warning_no_shutdown="0x""$voltage_warning_no_shutdown"
    voltage_warning_no_shutdown_l=$(($voltage_warning_no_shutdown & 0xf))
    voltage_warning_no_shutdown_l=$(printf %d "$voltage_warning_no_shutdown_l")
    echo "Input voltage warning; no shutdown:             $voltage_warning_no_shutdown_l" >> $OUTPUTFILE
    thermal_warning_no_shutdown_h=$((($voltage_warning_no_shutdown >> 4) & 0xf))
    thermal_warning_no_shutdown_h=$(printf %d "$thermal_warning_no_shutdown_h")
    echo "Thermal warning; no shutdown:                   $thermal_warning_no_shutdown_h" >> $OUTPUTFILE

    current_power_warning_no_shutdown=${value:257:2}
    current_power_warning_no_shutdown="0x""$current_power_warning_no_shutdown"
    current_power_warning_no_shutdown_l=$(($current_power_warning_no_shutdown & 0xf))
    current_power_warning_no_shutdown_l=$(printf %d "$current_power_warning_no_shutdown_l")
    echo "Output current power warning; no shutdown:      $current_power_warning_no_shutdown_l" >> $OUTPUTFILE
    fan_slow_warning_no_shutdown_h=$((($current_power_warning_no_shutdown >> 4) & 0xf))
    fan_slow_warning_no_shutdown_h=$(printf %d "$fan_slow_warning_no_shutdown_h")
    echo "Fan slow warning; no shutdown:                  $fan_slow_warning_no_shutdown_h" >> $OUTPUTFILE
}

do_action_all()
{
    prn_sta=$(get_psu_present "$device")
    if [ "$prn_sta" -eq 0 ];then
        echo "psu$device absent , please dump present psu"
        exit 1
    fi

    full_addr=${PSU_PMBUS_SLAVE_ADDR_ARRAY[$device-1]}
    decompose_iic_addr "${full_addr}"

    echo "MFR_BLACKBOX_PAGE =                             0x00 " >> $OUTPUTFILE
    i2cset -f -y $bus $address $BLACKBOX_PAGE_ADDRESS 0x00
    do_action

    echo " " >> $OUTPUTFILE
    echo "MFR_BLACKBOX_PAGE =                             0x01 " >> $OUTPUTFILE
    i2cset -f -y $bus $address $BLACKBOX_PAGE_ADDRESS 0x01
    do_action

    echo " " >> $OUTPUTFILE
    echo "MFR_BLACKBOX_PAGE =                             0x02 " >> $OUTPUTFILE
    i2cset -f -y $bus $address $BLACKBOX_PAGE_ADDRESS 0x02
    do_action

    echo " " >> $OUTPUTFILE
    echo "MFR_BLACKBOX_PAGE =                             0x03 " >> $OUTPUTFILE
    i2cset -f -y $bus $address $BLACKBOX_PAGE_ADDRESS 0x03
    do_action

    echo " " >> $OUTPUTFILE
    echo "MFR_BLACKBOX_PAGE =                             0x04 " >> $OUTPUTFILE
    i2cset -f -y $bus $address $BLACKBOX_PAGE_ADDRESS 0x04
    do_action

    echo "dump psu$psu_num blackbox data to $OUTPUTFILE ok."
}

check_parameter "$@"
do_action_all

#!/bin/bash
#
# Copyright 2019-present Huaqin. All Rights Reserved.
#
# This program file 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; version 2 of the License.
#
# 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 in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#
#shellcheck disable=SC1091

source /usr/local/bin/openbmc-utils.sh

COME_CPLD_FW="0.01.06_COMe.vme"

PROGRAM=$(basename "$0")

# LCNUM=$(get_lc_num)
LCNUM=2

SET_COME_HEART_VALUE_1=0x3
SET_COME_HEART_VALUE_2=0x4

COME_HEART_FAILED_COUNTS_LIMIT=3
COME_UPGRADE_FAILED_COUNTS_LIMIT=3

come_heart_failed_counts=()
come_upgrade_failed_counts=()

come_cpld_hotswap_flag=()

check_file_ok()
{
    if [ ! -f "$COME_CPLD_FW" ]; then
        logger -p user.err -t "$PROGRAM" "$COME_CPLD_FW doesnot exist, exit!!!"
        echo "$PROGRAM:" "$COME_CPLD_FW doesnot exist, exit!!!"
        exit 255
    fi
}

init_failed_counts()
{
    for((index=1;index<="${LCNUM}";index++));
    do
        come_heart_failed_counts[$index]=0
        come_upgrade_failed_counts[$index]=0
    done
}

check_come_cpld_hotswap()
{
    come_cpld_hotswap_flag[$index]=false
    # come_cpld_hotswap_flag[$index]=true
}

check_come_cpld_status()
{
    if "${come_cpld_hotswap_flag[$index]}" ; then
        come_upgrade_failed_counts[$index]=0
    fi

    lc_prnt=$(get_lc_present "${index}")
    if [ "${lc_prnt}" -eq 1 ];then
        come_sta=$(is_come_power_ok "${index}")
        if [ "${come_sta}" -eq 1 ];then
            if [[ "${come_upgrade_failed_counts[$index]}" < "$COME_UPGRADE_FAILED_COUNTS_LIMIT" ]] ; then
                check_heart_failed_count
            fi
        fi
    else
        come_upgrade_failed_counts[$index]=0
    fi
}

check_heart_failed_count()
{
    for((come_heart_failed_counts[$index]=0 ; ${come_heart_failed_counts[$index]}<"$COME_HEART_FAILED_COUNTS_LIMIT" ; come_heart_failed_counts[$index]++));
    do
        come_scratch_path="${LCCOME_PATH_DICT[$index]}/come_scratch"
        echo "$SET_COME_HEART_VALUE_1" > "$come_scratch_path"
        read_value_1=$(head -n 1 < "$come_scratch_path")
        sleep 5
        echo "$SET_COME_HEART_VALUE_2" > "$come_scratch_path"
        read_value_2=$(head -n 1 < "$come_scratch_path")

        if [ "$read_value_1" == "$SET_COME_HEART_VALUE_1" ] && [ "$read_value_2" == "$SET_COME_HEART_VALUE_2" ]; then
            come_heart_failed_counts[$index]=0
            come_upgrade_failed_counts[$index]=0
            break
        fi
    done

    if [ "${come_heart_failed_counts[$index]}" == "$COME_HEART_FAILED_COUNTS_LIMIT" ] ; then
        come_heart_failed_counts[$index]=0
        check_upgrade_failed_count
    fi
}

check_upgrade_failed_count()
{
    for((come_upgrade_failed_counts[$index]=0 ; ${come_upgrade_failed_counts[$index]}<"$COME_UPGRADE_FAILED_COUNTS_LIMIT" ; come_upgrade_failed_counts[$index]++));
    do
        if ! /usr/local/bin/upgrade_cpld -d come -f $COME_CPLD_FW -i $index; then
            reboot_str=$(reboot_cpu -i $index -o cycle)
            if echo "$reboot_str" | grep Success > /dev/null 2>&1 ; then
                echo "$SET_COME_HEART_VALUE_1" > "$come_scratch_path"
                read_value=$(head -n 1 < "$come_scratch_path")
                if [ "$read_value" == "$SET_COME_HEART_VALUE_1" ];then
                    come_heart_failed_counts[$index]=0
                    come_upgrade_failed_counts[$index]=0
                    break
                fi
            fi
        fi
    done

    if [ "${come_upgrade_failed_counts[$index]}" == "$COME_UPGRADE_FAILED_COUNTS_LIMIT" ] ; then
        logger -p user.err -t "$PROGRAM:" "come$index cpld upgrade failed times has exceeded limit value $COME_UPGRADE_FAILED_COUNTS_LIMIT"
    fi
}

check_file_ok
init_failed_counts
while true
do
    for((index=1;index<="${LCNUM}";index++));
    do
        check_come_cpld_hotswap
        check_come_cpld_status
        sleep 5
    done
    sleep 6 # 60
done

test.sh

#!/bin/bash
# 设置切波,功率,echo ,cat
function help_usage(){

	echo "Usage: test.sh [OPTIONS]"
	echo "Available options:"
	echo "-h [options help]  help"
	echo "-d [value]	   device num"
	echo "-p [value]         power"
	echo "-f [value]         fine-tuning enble"
	echo "-o [value]         offset"
	echo "-c [value]         channle"
	echo "-n [value]         freq_n_value"
	echo "-l [value]         laser on/off"
	echo "-m [messages]      messages"
	echo "-s [status]        status"
}
ARGS=`getopt -o hd:p:sc:mn:f:o:l: -n "$0" -- "$@"`
PRAM="flase"
POWER="flase"
STATUS="flase"
CHANNLE="flase"
FREQ="flase"
MESSAGES="flase"
LASER="flase"
FINETUNING="flase"
OFFSET="flase"

while true
do
	case "$1" in
		-h)
		help_usage
		exit 0
		;;
		-d)
			case "$2" in
				"")
					echo -e "ERROR:NO device number please input 0-15"
					help_usage
					exit 1
					;;
				*)
					val=$2 
					#echo $val
					PRAM="true"	
					shift  2
					;;
			esac
		;;
		-p)
			case "$2" in
				"")
					echo -e "Woring: NO value,please input low or high"
					;;
				*)
				value=$2
			#	echo $value
				POWER="true"
				shift 2
				;;
			esac
			;;
		-s)
		STATUS="true"
		break
		;;

		-m)
		MESSAGES="true"
		break 
		;;
		-f)
			case "$2" in
				"")
					echo -e "ERROR:NO param please input enble or disable"
					help_usage
					exit 1
					;;
				*)
					FINETUNING="true"
					finetuning=$2 
					shift  2
					;;
			esac
		;;
		-o)
			case "$2" in
				"")
					echo -e "ERROR:NO param please input offset of fine-tuning"
					echo -e "fine-tuning offset range low:-5000 high:5000" 
					help_usage
					exit 1
					;;
				*)
					OFFSET="true"
					offset=$2 
					shift  2
					;;
			esac
		;;
		-c)
			case "$2" in
				"")
					echo -e "ERROR:NO channle module please input"
					echo -e "channle module support 3.125 6.25 12.5 25 50 75 100ghz,33ghz not support " 
					help_usage
					exit 1
					;;
				*)
					CHANNLE="true"
					channle=$2 
					shift  2
					;;
			esac
		;;
		-n)
			case "$2" in
				"")
					echo -e "ERROR:NO frequency param please input"
					help_usage
					exit 1
					;;
				*)
					FREQ="true"
					frequency=$2 
					shift  2
					;;
			esac
		;;
		-l)
			case "$2" in
				"")
					echo -e "ERROR:NO param please input on or off"
					help_usage
					exit 1
					;;
				*)
					LASER="true"
					laser=$2 
					shift  2
					;;
			esac
		;;
		"")
			if [ "$PRAM" = "true" ] ;then
				break
			else
				help_usage
		        	exit 0	
			fi
			;;
		*)
		help_usage
		exit 0
		;;
			
	esac
done

BASE_INFO="base_info"
WAVE_SWITCH="wavelength_switch"
dev=14
bus=`expr ${val} + ${dev}`
#echo $bus

if [ $bus -ge 14 ] && [ $bus -le 29 ] && [ "$PRAM" = "true" ];then
	device_path="/sys/bus/i2c/drivers/zr_optoe/$bus-0050/"
else
	echo " input port num is error"
	exit 0
fi

if [  -d "$device_path" ];then
	module_type=`cd $device_path$BASE_INFO && cat module_type`
	if [ $module_type != 0x1 ];then
		echo " the port nmmber $val is not color module"
		exit 0
	fi
fi

if [  -d "$device_path" ] && [ "$FINETUNING" = "true" ] && [ "$OFFSET" = "true" ] ;then
	datapath_status=`cd $device_path$BASE_INFO && cat lane1_host_status`
	if [ $offset -gt 5000 ] || [ $offset -lt -5000 ];then
	       echo "fine-tuning offset out of range, low:-5000 high:5000"
	       exit 0
        fi	       
	if [ $datapath_status = 0x1 ] || [ $datapath_status = 0x4 ] || [ $datapath_status = 0x7 ];then		
		if [ $finetuning != "enble" ] && [ $finetuning != "disable" ];then
			echo " fine-tuning switch value is error,enble or disable"
			exit 1
		elif [ $finetuning = "enble" ];then
			cd $device_path$WAVE_SWITCH && echo 0x1 > lane1_ftg_enable && echo $offset > lane1_ftg_offset
			old_freq=`cd $device_path$WAVE_SWITCH && cat lane1_cha_num`
		#	old_spac=`cd $device_path$WAVE_SWITCH && cat lane1_grid_spac`
			cd $device_path$WAVE_SWITCH && echo $old_freq > lane1_cha_num 
			echo "fine-tuning change success"
		else	
			cd $device_path$WAVE_SWITCH && echo 0x0 > lane1_ftg_enable && echo $offset > lane1_ftg_offset
			echo "fine-tuning turn off  success"		
		fi
	else
		echo "the color module datapath status can't change frequency "
		exit 0
	fi 	
else
	if [ "$FINETUNING" = "true" ];then
	     echo "device is not exist"
	     exit 1
        fi
fi

if [  -d "$device_path" ] && [ "$POWER" = "true" ];then
	oldvalue=`cd $device_path$BASE_INFO && cat ctrl_power`
	if [ $value != "high" ] && [ $value != "low" ];then
		echo " power value is error,high or low"
		exit 1
	elif [ $oldvalue = 0x0 ] && [ $value = "high" ];then
		echo "It's already high power"
		exit 1
	elif [ $oldvalue != 0x0 ] && [ $value = "low" ];then
		echo "it's already low power"
		exit 1
	elif [ $oldvalue != 0x0 ] && [ $value = "high" ];then
		cd $device_path$BASE_INFO && echo 0x0 > ctrl_power
		sleep 0.5
		high_power=`cd $device_path$BASE_INFO && cat ctrl_power`
		if [ $high_power = 0x0 ];then
			echo "change high power success"
			echo "high power"
		else
			echo "change high power fail"
		fi
		exit 0
	else
		cd $device_path$BASE_INFO && echo 0x40 > ctrl_power
		sleep 0.5
		low_power=`cd $device_path$BASE_INFO && cat ctrl_power`
		if [ $low_power = 0x40 ];then
			echo "change low power success"
			echo "low power"
		else
			echo "change low power fail"
		fi
		exit 0
	fi
else
	if [ "$POWER" = "true" ];then
	     echo "device is not exist"
	     exit 1
        fi
fi

if [ -d "$device_path" ] && [ "$CHANNLE" = "true" ] && [ "$FREQ" = "true" ];then
	datapath_status=`cd $device_path$BASE_INFO && cat lane1_host_status`
	if [ $datapath_status = 0x1 ] || [ $datapath_status = 0x4 ] || [ $datapath_status = 0x7 ];then
		case $channle in
			3.125)
				cd $device_path$WAVE_SWITCH && echo 0x0 > lane1_grid_spac						
				if [ $frequency -ge -576 ] && [ $frequency -le 960 ];then				
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num	
					echo "change 3.125ghz frequency success"	
				else
					echo " freqency param n is out of range,check or cmd -m"
					exit 0
				fi
			;;
			6.25)
				cd $device_path$WAVE_SWITCH && echo 0x1 > lane1_grid_spac
				
				if [ $frequency -ge -288 ] && [ $frequency -le 480 ];then			
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num				
					echo "change 6.25ghz frequency success"
				else
					echo " freqency param n is out of range,check or cmd -m"
					exit 0
				fi
				;;
			12.5)
				cd $device_path$WAVE_SWITCH && echo 0x2 > lane1_grid_spac
				
				if [ $frequency -ge -144 ] && [ $frequency -le 240 ];then				
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num								
					echo "change 12.5ghz frequency fail"
				else
					echo " freqency param n is out of range,check or cmd -m"
					exit 0
				fi
				;;
			25)
				cd $device_path$WAVE_SWITCH && echo 0x3 > lane1_grid_spac
				
				if [ $frequency -ge -72 ] && [ $frequency -le 120 ];then				
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num								
					echo "change 25ghz frequency success"
				else
					echo " freqency param n is out of range,check or cmd -m"
					exit 0
				fi
				;;
			50)
				cd $device_path$WAVE_SWITCH && echo 0x4 > lane1_grid_spac			
				if [ $frequency -ge -36 ] && [ $frequency -le 60 ];then				
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num											
					echo "change 50ghz frequency success"
				else
					echo " freqency param n is out of range,check or cmd -m"
					exit 0
				fi
				;;
			75)
				cd $device_path$WAVE_SWITCH && echo 0x6 > lane1_grid_spac			
				right_param=`expr $frequency % 3`
				if [ $frequency -ge -72 ] && [ $frequency -le 120 ] && [ $right_param = 0 ];then								
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num				
					echo "change 75ghz frequency success"
				else
					echo " freqency param n is out of range,or param error,check or cmd -m"
					
					exit 0
				fi
				;;
			100)
				cd $device_path$WAVE_SWITCH && echo 0x5 > lane1_grid_spac
				
				if [ $frequency -ge -18 ] && [ $frequency -le 30 ];then				
					cd $device_path$WAVE_SWITCH && echo $frequency > lane1_cha_num										
					echo "change 100ghz frequency success"
				else
					echo " freqency param n is out of range,check or cmd -m for detail messages"
					exit 0
				fi
				;;
			*)    echo "channle param is error ,check or cmd -m for detail messages"
				;;
	esac
	else
		echo "the color module datapath status can't change frequency "
		exit 0
	fi	
else
	if [ "$CHANNNLE" = "true" ];then
	     echo "device is not exist"
	     exit 1
        fi
fi

if [ -d "$device_path" ] && [ "$LASER" = "true" ];then
	if [ $laser != "on" ] && [ $laser != "off" ];then
		echo " laser switch value is error,on or off"
		exit 1
	elif [ $laser = "on" ];then
	   	cd $device_path$BASE_INFO && echo 0x0 > laser_on_off && echo 0x0 > tx1_disable				
		echo "laser turn on success"
	else	
	   	cd $device_path$BASE_INFO && echo 0xff > laser_on_off && echo 0x1 > tx1_disable		
			echo "laser turn off  success"		
	fi	
else
	if [ "$LASER" = "true" ];then
	     echo "device is not exist"
        fi
fi

if [ -d "$device_path" ] && [ "$STATUS" = "true" ];then
	echo "QDD$val is color module and exist,following detail messages"
	ftg_enble=`cd $device_path$WAVE_SWITCH && cat lane1_ftg_enable` 
	ftg_offset=`cd $device_path$WAVE_SWITCH && cat lane1_ftg_offset` 
	if [ $ftg_enble = 0x1 ];then
		echo "current fine-tuning is turn on,the offset is $ftg_offset"
	else
		echo "current fine-tuning is turn off,the offset is $ftg_offset"
	fi
	 grid_spac=`cd $device_path$WAVE_SWITCH && cat lane1_grid_spac` 
	 if [ $grid_spac = 0x0 ];then
		     echo "current channle is 3.125ghz"
	 	     grid_spac=3.125
	 elif [ $grid_spac = 0x1 ];then
		     echo "current channle is 6.25ghz"
	 	     grid_spac=6.25
	 elif [ $grid_spac = 0x2 ];then
		     echo "current channle is 12.5ghz"
	 	     grid_spac=12.5
	 elif [ $grid_spac = 0x3 ];then
		     echo "current channle is 25ghz"
	 	     grid_spac=25
	 elif [ $grid_spac = 0x4 ];then
		     echo "current channle is 50ghz"
	 	     grid_spac=50 
	 elif [ $grid_spac = 0x5 ];then
		     echo "current channle is 100ghz"
	 	     grid_spac=100 
	 elif [ $grid_spac = 0x6 ];then
		     echo "current channle is 75ghz"
	 	     grid_spac=75 
	 else
		 echo "current channle is not support"
	 fi
	cha_value=`cd $device_path$WAVE_SWITCH && cat lane1_cha_num`
	echo "the $grid_spac ghz n value is $cha_value"
	current_freq=`cd $device_path$WAVE_SWITCH && cat lane1_cur_freq` 
	echo "the $grid_spac ghz current frequency is $current_freq"
	laser_on=`cd $device_path$BASE_INFO && cat laser_on_off` 
	laser_enble=`cd $device_path$BASE_INFO && cat tx1_disable` 
	if [ $laser_on = 0x0 ] && [ $laser_enble = 0x0 ];then
		echo "current laser status turn on already"
	else
		echo "current laser status is turn off "
	fi
	high_power=`cd $device_path$BASE_INFO && cat ctrl_power`
	if [ $high_power = 0x0 ];then
		echo "current device status high power"
	else
		echo "current device status low power"
	fi
	tx1_power=`cd $device_path$BASE_INFO && cat tx1_power` 
	echo "QDD$val tx1_power is $tx1_power"
	rx1_power=`cd $device_path$BASE_INFO && cat rx1_power` 
	echo "QDD$val rx1_power is $rx1_power"
	 datapath_status=`cd $device_path$BASE_INFO && cat lane1_host_status` 
	 if [ $datapath_status = 0x1 ];then
		     echo "datapath status is DataPathDeactivated"
	 elif [ $datapath_status = 0x2 ];then
		     echo "datapath status is DataPathInit"
	 elif [ $datapath_status = 0x3 ];then
		     echo "datapath status is DataPathDeinit"
	 elif [ $datapath_status = 0x4 ];then
		     echo "datapath status is DataPathActivated"
	 elif [ $datapath_status = 0x5 ];then
		     echo "datapath status is DataPathTxTurnOn"
	 elif [ $datapath_status = 0x6 ];then
		     echo "datapath status is DataPathTxTurnOff"
	 elif [ $datapath_status = 0x7 ];then
		     echo "datapath status is DataPathInitialized"
	 else
		     echo "datapath status is Reserved"
	 fi
else
	if [ "$STATUS" = "true" ];then
	     echo "device is not exist"
	     exit 1
        fi
fi

if [ -d "$device_path" ] && [ "$MESSAGES" = "true" ];then
	echo "here are funetuning offset range low:-5000 high:5000"
	echo "here are channle whether support and channle frequency n range!"
	echo "3.125G SUPPORT: channle n low = -576,channle n high = 960"
	echo "3.125G FREQ =193.1 + n*0.003125"
	echo "6.25G  SUPPORT: channle n low = -288,channle n high = 480"
	echo "6.25G FREQ =193.1 + n*0.00625"
	echo "12.5G  SUPPORT: channle n low = -144,channle n high = 240"
	echo "12.5G FREQ =193.1 + n*0.0125"
	echo "25G    SUPPORT: channle n low = -72, channle n high = 120"
	echo "25G FREQ =193.1 + n*0.025"
	echo "50G    SUPPORT: channle n low = -36, channle n high = 60"
	echo "50G FREQ =193.1 + n*0.05"
	echo "75G    SUPPORT: channle n low = -72, channle n high = 120"
	echo "75G FREQ =193.1 + n*0.025 n%3=0"
	echo "100G   SUPPORT: channle n low = -18, channle n high = 30"
	echo "100G FREQ =193.1 + n*0.1"
	echo "33G NOT SUPPORT: channle n low = -54, channle n high = 90"
else
	if [ "$MESSAGES" = "true" ];then
	     echo "device is not exist"
	    # echo 555
	     exit 1
        fi
fi

def.h

hwmon-sysfs.h

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 *  hwmon-sysfs.h - hardware monitoring chip driver sysfs defines   电压电流传感器头文件
 *
 *  Copyright (C) 2005 Yani Ioannou 
 */
#ifndef _LINUX_HWMON_SYSFS_H
#define _LINUX_HWMON_SYSFS_H

#include 

struct sensor_device_attribute {
    struct device_attribute dev_attr;
    int index;
};
#define to_sensor_dev_attr(_dev_attr) \
    container_of(_dev_attr, struct sensor_device_attribute, dev_attr)

#define SENSOR_ATTR(_name, _mode, _show, _store, _index) \
    {                                                    \
        .dev_attr = __ATTR(_name, _mode, _show, _store), \
        .index = _index                                  \
    }

#define SENSOR_ATTR_RO(_name, _func, _index) \
    SENSOR_ATTR(_name, 0444, _func##_show, NULL, _index)

#define SENSOR_ATTR_RW(_name, _func, _index) \
    SENSOR_ATTR(_name, 0644, _func##_show, _func##_store, _index)

#define SENSOR_ATTR_WO(_name, _func, _index) \
    SENSOR_ATTR(_name, 0200, NULL, _func##_store, _index)

#define SENSOR_DEVICE_ATTR(_name, _mode, _show, _store, _index) \
    struct sensor_device_attribute sensor_dev_attr_##_name      \
        = SENSOR_ATTR(_name, _mode, _show, _store, _index)

#define SENSOR_DEVICE_ATTR_RO(_name, _func, _index) \
    SENSOR_DEVICE_ATTR(_name, 0444, _func##_show, NULL, _index)

#define SENSOR_DEVICE_ATTR_RW(_name, _func, _index) \
    SENSOR_DEVICE_ATTR(_name, 0644, _func##_show, _func##_store, _index)

#define SENSOR_DEVICE_ATTR_WO(_name, _func, _index) \
    SENSOR_DEVICE_ATTR(_name, 0200, NULL, _func##_store, _index)

struct sensor_device_attribute_2 {
    struct device_attribute dev_attr;
    u8 index;
    u8 nr;
};
#define to_sensor_dev_attr_2(_dev_attr) \
    container_of(_dev_attr, struct sensor_device_attribute_2, dev_attr)

#define SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index) \
    {                                                           \
        .dev_attr = __ATTR(_name, _mode, _show, _store),        \
        .index = _index,                                        \
        .nr = _nr                                               \
    }

#define SENSOR_ATTR_2_RO(_name, _func, _nr, _index) \
    SENSOR_ATTR_2(_name, 0444, _func##_show, NULL, _nr, _index)

#define SENSOR_ATTR_2_RW(_name, _func, _nr, _index) \
    SENSOR_ATTR_2(_name, 0644, _func##_show, _func##_store, _nr, _index)

#define SENSOR_ATTR_2_WO(_name, _func, _nr, _index) \
    SENSOR_ATTR_2(_name, 0200, NULL, _func##_store, _nr, _index)

#define SENSOR_DEVICE_ATTR_2(_name, _mode, _show, _store, _nr, _index) \
    struct sensor_device_attribute_2 sensor_dev_attr_##_name           \
        = SENSOR_ATTR_2(_name, _mode, _show, _store, _nr, _index)

#define SENSOR_DEVICE_ATTR_2_RO(_name, _func, _nr, _index) \
    SENSOR_DEVICE_ATTR_2(_name, 0444, _func##_show, NULL, _nr, _index)

#define SENSOR_DEVICE_ATTR_2_RW(_name, _func, _nr, _index) \
    SENSOR_DEVICE_ATTR_2(_name, 0644, _func##_show, _func##_store, _nr, _index)

#define SENSOR_DEVICE_ATTR_2_WO(_name, _func, _nr, _index) \
    SENSOR_DEVICE_ATTR_2(_name, 0200, NULL, _func##_store, _nr, _index)

#endif /* _LINUX_HWMON_SYSFS_H */

Makefile

obj-m := cpld_lpc.o cpld_i2c.o  i2c-ocores.o mc24lc64t.o optoe.o fpga_i2c_adapter.o switchboard.o \
ina2xx.o pxe1410.o i2c_dev_sysfs.o i2c_kobj_sysfs.o   sn1701022.o xdpe12284.o zr_optoe.o

all:
	#make -C /mnt/sda/daili/haishen/lcj/wangjian/header/usr/src/linux-headers-4.9.0-14-2-amd64 M=$(PWD) modules
	make -C /home/4.9-head/header/usr/src/linux-headers-4.9.0-14-2-amd64 M=$(PWD) modules
clean:

	#make -C /mnt/sda/daili/haishen/lcj/wangjian/header/usr/src/linux-headers-4.9.0-14-2-amd64 M=$(PWD)  clean
	make -C /home/4.9-head/header/usr/src/linux-headers-4.9.0-14-2-amd64 M=$(PWD)  clean

mc24lc64t.c

/*================================================================
 *   Copyright (C) 2020,Huaqin Technology Co.,Ltd.
 *                 All rights reserved.
 *   Building 10, No, 399, Keyuan Road, Pudong, Shanghai
 *
 *
 *   File Name   : mc24lc64t.c
 *   Author      : carl.wang ([email protected])
 *   Created Time: Thu Dec 14 16:12:51 2020
 *   Description : mc24lc64t.c -- the driver for Microchip 24LC64T.
 *   Copyright (C) 2020 Huaqin Corp.
 *
 *   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 0 of the License, Or (at
 *   your option) any later version.
 *
 ================================================================*/
//e2rom驱动,接在i2c上device,device和驱动匹配调probe
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define EEPROM_SIZE 8192 //mc24lt64t eeprom size in bytes.

struct mc24lc64t_data {
    struct mutex update_lock;
};

static ssize_t mc24lc64t_read(
    struct file *filp,
    struct kobject *kobj,
    struct bin_attribute *bin_attr,
    char *buf,
    loff_t off,
    size_t count)
{
    struct i2c_client *client = kobj_to_i2c_client(kobj);
    struct mc24lc64t_data *drvdata = i2c_get_clientdata(client);
    size_t timeout, read_time, i = 0;
    int32_t status;

    if (drvdata == NULL)
        return -ENODEV;

    if (i2c_smbus_write_byte_data(client, off >> 8, off)) {
        status = -EIO;
        goto exit;
    }
    msleep(1);

    mutex_lock(&drvdata->update_lock);

begin:
    if (i < count) {
        timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/
        do {
            read_time = jiffies;
            status = i2c_smbus_read_byte(client);
            if (status >= 0) {
                buf[i++] = status;
                goto begin;
            }
        } while (time_before(read_time, timeout));
        status = -ETIMEDOUT;
        goto exit;
    }
    status = count;
exit:

    mutex_unlock(&drvdata->update_lock);
    return status;
}

static ssize_t mc24lc64t_write(
    struct file *filp,
    struct kobject *kobj,
    struct bin_attribute *bin_attr,
    char *buf,
    loff_t off,
    size_t count)
{
    struct i2c_client *client = kobj_to_i2c_client(kobj);
    struct mc24lc64t_data *drvdata = i2c_get_clientdata(client);
    size_t timeout, write_time, i = 0;
    int32_t status;
    u16 value;

    if (drvdata == NULL)
        return -ENODEV;

    mutex_lock(&drvdata->update_lock);

begin:
    if (i < count) {
        timeout = jiffies + msecs_to_jiffies(25); /* 25 mS timeout*/
        value = (buf[i] << 8 | (off & 0xff));
        do {
            write_time = jiffies;
            status = i2c_smbus_write_word_data(client, off >> 8, value);
            if (status == 0) {
                /* increase offset */
                off++;
                /* increase buffer index */
                i++;
                goto begin;
            }
        } while (time_before(write_time, timeout));
        status = -ETIMEDOUT;
        goto exit;
    }
    status = count;

exit:

    mutex_unlock(&drvdata->update_lock);
    return status;
}

static struct bin_attribute mc24lc64t_bit_attr = {
    .attr = {
        .name = "eeprom",
        .mode = S_IRUGO | S_IWUGO,
    },
    .size = EEPROM_SIZE,
    .read = mc24lc64t_read,
    .write = mc24lc64t_write,
};

static int32_t mc24lc64t_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct i2c_adapter *adapter = client->adapter;
    struct mc24lc64t_data *drvdata;
    int32_t err;

    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_READ_BYTE))
        return -EPFNOSUPPORT;

    if (!(drvdata = devm_kzalloc(&client->dev, sizeof(struct mc24lc64t_data), GFP_KERNEL)))
        return -ENOMEM;

    i2c_set_clientdata(client, drvdata);
    mutex_init(&drvdata->update_lock);

    err = sysfs_create_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr);
    return err;
}

static int32_t mc24lc64t_remove(struct i2c_client *client)
{
    sysfs_remove_bin_file(&client->dev.kobj, &mc24lc64t_bit_attr);
    return 0;
}

static const struct i2c_device_id mc24lc64t_id[] = {{"24lc64t", 0}, {}};
MODULE_DEVICE_TABLE(i2c, mc24lc64t_id);

static struct i2c_driver mc24lc64t_driver = {
    .driver = {
        .name = "mc24lc64t",
        .owner = THIS_MODULE,
    },
    .probe = mc24lc64t_probe,
    .remove = mc24lc64t_remove,
    .id_table = mc24lc64t_id,
};

module_i2c_driver(mc24lc64t_driver);

MODULE_AUTHOR("Huaqin Technology Co.,Ltd.");
MODULE_DESCRIPTION("Hua Qin 24LC64T Driver");
MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL");

optoe.c

/*
 * optoe.c - A driver to read and write the EEPROM on optical transceivers
 * (SFP, QSFP and similar I2C based devices)
 *
 * Copyright (C) 2014 Cumulus networks Inc.
 * Copyright (C) 2017 Finisar Corp.
 *
 * 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 Freeoftware Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

/*
 *    Description:
 *    a) Optical transceiver EEPROM read/write transactions are just like
 *        the at24 eeproms managed by the at24.c i2c driver
 *    b) The register/memory layout is up to 256 128 byte pages defined by
 *        a "pages valid" register and switched via a "page select"
 *        register as explained in below diagram.
 *    c) 256 bytes are mapped at a time. 'Lower page 00h' is the first 128
 *            bytes of address space, and always references the same
 *            location, independent of the page select register.
 *            All mapped pages are mapped into the upper 128 bytes
 *            (offset 128-255) of the i2c address.
 *    d) Devices with one I2C address (eg QSFP) use I2C address 0x50
 *        (A0h in the spec), and map all pages in the upper 128 bytes
 *        of that address.
 *    e) Devices with two I2C addresses (eg SFP) have 256 bytes of data
 *        at I2C address 0x50, and 256 bytes of data at I2C address
 *        0x51 (A2h in the spec).  Page selection and paged access
 *        only apply to this second I2C address (0x51).
 *    e) The address space is presented, by the driver, as a linear
 *            address space.  For devices with one I2C client at address
 *            0x50 (eg QSFP), offset 0-127 are in the lower
 *            half of address 50/A0h/client[0].  Offset 128-255 are in
 *            page 0, 256-383 are page 1, etc.  More generally, offset
 *            'n' resides in page (n/128)-1.  ('page -1' is the lower
 *            half, offset 0-127).
 *    f) For devices with two I2C clients at address 0x50 and 0x51 (eg SFP),
 *        the address space places offset 0-127 in the lower
 *            half of 50/A0/client[0], offset 128-255 in the upper
 *            half.  Offset 256-383 is in the lower half of 51/A2/client[1].
 *            Offset 384-511 is in page 0, in the upper half of 51/A2/...
 *            Offset 512-639 is in page 1, in the upper half of 51/A2/...
 *            Offset 'n' is in page (n/128)-3 (for n > 383)
 *
 *                        One I2c addressed (eg QSFP) Memory Map
 *
 *                        2-Wire Serial Address: 1010000x
 *
 *                        Lower Page 00h (128 bytes)
 *                        =====================
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |                     |
 *                       |Page Select Byte(127)|
 *                        =====================
 *                                  |
 *                                  |
 *                                  |
 *                                  |
 *                                  V
 *         ------------------------------------------------------------
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        |                 |                  |                       |
 *        V                 V                  V                       V
 *     ------------   --------------      ---------------     --------------
 *    |            | |              |    |               |   |              |
 *    |   Upper    | |     Upper    |    |     Upper     |   |    Upper     |
 *    |  Page 00h  | |    Page 01h  |    |    Page 02h   |   |   Page 03h   |
 *    |            | |   (Optional) |    |   (Optional)  |   |  (Optional   |
 *    |            | |              |    |               |   |   for Cable  |
 *    |            | |              |    |               |   |  Assemblies) |
 *    |    ID      | |     AST      |    |      User     |   |              |
 *    |  Fields    | |    Table     |    |   EEPROM Data |   |              |
 *    |            | |              |    |               |   |              |
 *    |            | |              |    |               |   |              |
 *    |            | |              |    |               |   |              |
 *     ------------   --------------      ---------------     --------------
 *
 * The SFF 8436 (QSFP) spec only defines the 4 pages described above.
 * In anticipation of future applications and devices, this driver
 * supports access to the full architected range, 256 pages.
 *
 * The CMIS (Common Management Interface Specification) defines use of
 * considerably more pages (at least to page 0xAF), which this driver
 * supports.
 *
 * NOTE: This version of the driver ONLY SUPPORTS BANK 0 PAGES on CMIS
 * devices.
 *
 **/

/* #define DEBUG 1 */

#undef EEPROM_CLASS
#ifdef CONFIG_EEPROM_CLASS
    #define EEPROM_CLASS
#endif
#ifdef CONFIG_EEPROM_CLASS_MODULE
    #define EEPROM_CLASS
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "i2c_kobj_sysfs.h"

#ifdef EEPROM_CLASS
    #include 
#endif

#include 

/* The maximum length of a port name */
#define MAX_PORT_NAME_LEN 20

struct optoe_platform_data {
    uint32_t byte_len;  /* size (sum of all addr) */
    uint16_t page_size; /* for writes */
    uint8_t flags;
    void *dummy1; /* backward compatibility */
    void *dummy2; /* backward compatibility */

#ifdef EEPROM_CLASS
    struct eeprom_platform_data *eeprom_data;
#endif
    char port_name[MAX_PORT_NAME_LEN];
};

/* fundamental unit of addressing for EEPROM */
#define OPTOE_PAGE_SIZE              128
/*
 * Single address devices (eg QSFP) have 256 pages, plus the unpaged
 * low 128 bytes.  If the device does not support paging, it is
 * only 2 'pages' long.
 */
#define OPTOE_ARCH_PAGES             256
#define ONE_ADDR_EEPROM_SIZE         ((1 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE)
#define ONE_ADDR_EEPROM_UNPAGED_SIZE (2 * OPTOE_PAGE_SIZE)
/*
 * Dual address devices (eg SFP) have 256 pages, plus the unpaged
 * low 128 bytes, plus 256 bytes at 0x50.  If the device does not
 * support paging, it is 4 'pages' long.
 */
#define TWO_ADDR_EEPROM_SIZE         ((3 + OPTOE_ARCH_PAGES) * OPTOE_PAGE_SIZE)
#define TWO_ADDR_EEPROM_UNPAGED_SIZE (4 * OPTOE_PAGE_SIZE)
#define TWO_ADDR_NO_0X51_SIZE        (2 * OPTOE_PAGE_SIZE)

/* a few constants to find our way around the EEPROM */
#define OPTOE_PAGE_SELECT_REG        0x7F
#define ONE_ADDR_PAGEABLE_REG        0x02
#define QSFP_NOT_PAGEABLE            BIT(2)
#define CMIS_NOT_PAGEABLE            BIT(7)
#define TWO_ADDR_PAGEABLE_REG        0x40
#define TWO_ADDR_PAGEABLE            BIT(4)
#define TWO_ADDR_0X51_REG            92
#define TWO_ADDR_0X51_SUPP           BIT(6)
#define OPTOE_ID_REG                 0
#define OPTOE_READ_OP                0
#define OPTOE_WRITE_OP               1
#define OPTOE_EOF                    0 /* used for access beyond end of device */

#define HEX2DEC_2_BYTE_LEN           7

#define NODE_DIR                     "optoe_test_node"

struct optoe_data {
    struct optoe_platform_data chip;
    int32_t use_smbus;
    char port_name[MAX_PORT_NAME_LEN];

    /*
     * Lock protects against activities from other Linux tasks,
     * but not from changes by other I2C masters.
     */
    struct mutex lock;
    struct bin_attribute bin;
    struct attribute_group attr_group;

    uint8_t *writebuf;
    uint32_t write_max;

    uint32_t num_addresses;

#ifdef EEPROM_CLASS
    struct eeprom_device *eeprom_dev;
#endif

    /* dev_class: ONE_ADDR (QSFP) or TWO_ADDR (SFP) */
    int32_t dev_class;

    struct i2c_client *client[];
};

/*
 * This parameter is to help this driver avoid blocking other drivers out
 * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
 * clock, one 256 byte read takes about 1/43 second which is excessive;
 * but the 1/170 second it takes at 400 kHz may be quite reasonable; and
 * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
 *
 * This value is forced to be a power of two so that writes align on pages.
 */
static uint32_t io_limit = OPTOE_PAGE_SIZE;

/*
 * specs often allow 5 msec for a page write, sometimes 20 msec;
 * it's important to recover from write timeouts.
 */
static uint32_t write_timeout = 25;

/*
 * flags to distinguish one-address (QSFP family) from two-address (SFP family)
 * If the family is not known, figure it out when the device is accessed
 */
#define ONE_ADDR  1
#define TWO_ADDR  2
#define CMIS_ADDR 3

static const struct i2c_device_id optoe_ids[] = {{"optoe1", ONE_ADDR}, {"optoe2", TWO_ADDR}, {"optoe3", CMIS_ADDR}, {"sff8436", ONE_ADDR}, {"24c04", TWO_ADDR}, {/* END OF LIST */}};
MODULE_DEVICE_TABLE(i2c, optoe_ids);

/*-------------------------------------------------------------------------*/
/*
 * This routine computes the addressing information to be used for
 * a given r/w request.
 *
 * Task is to calculate the client (0 = i2c addr 50, 1 = i2c addr 51),
 * the page, and the offset.
 *
 * Handles both single address (eg QSFP) and two address (eg SFP).
 *     For SFP, offset 0-255 are on client[0], >255 is on client[1]
 *     Offset 256-383 are on the lower half of client[1]
 *     Pages are accessible on the upper half of client[1].
 *     Offset >383 are in 128 byte pages mapped into the upper half
 *
 *     For QSFP, all offsets are on client[0]
 *     offset 0-127 are on the lower half of client[0] (no paging)
 *     Pages are accessible on the upper half of client[1].
 *     Offset >127 are in 128 byte pages mapped into the upper half
 *
 *     Callers must not read/write beyond the end of a client or a page
 *     without recomputing the client/page.  Hence offset (within page)
 *     plus length must be less than or equal to 128.  (Note that this
 *     routine does not have access to the length of the call, hence
 *     cannot do the validity check.)
 *
 * Offset within Lower Page 00h and Upper Page 00h are not recomputed
 */

static uint8_t optoe_translate_offset(struct optoe_data *optoe, loff_t *offset, struct i2c_client **client)
{
    uint32_t page = 0;

    *client = optoe->client[0];

    /* if SFP style, offset > 255, shift to i2c addr 0x51 */
    if (optoe->dev_class == TWO_ADDR) {
        if (*offset > 255) {
            /* like QSFP, but shifted to client[1] */
            *client = optoe->client[1];
            *offset -= 256;
        }
    }

    /*
     * if offset is in the range 0-128...
     * page doesn't matter (using lower half), return 0.
     * offset is already correct (don't add 128 to get to paged area)
     */
    if (*offset < OPTOE_PAGE_SIZE)
        return page;

    /* note, page will always be positive since *offset >= 128 */
    page = (*offset >> 7) - 1;
    /* 0x80 places the offset in the top half, offset is last 7 bits */
    *offset = OPTOE_PAGE_SIZE + (*offset & 0x7f);

    return page; /* note also returning client and offset */
}

static ssize_t
optoe_eeprom_read(struct optoe_data *optoe, struct i2c_client *client, char *buf, uint32_t offset, size_t count)
{
    struct i2c_msg msg[2];
    uint8_t msgbuf[2];
    size_t timeout, read_time;
    int32_t status, i;

    memset(msg, 0, sizeof(msg));

    switch (optoe->use_smbus) {
        case I2C_SMBUS_I2C_BLOCK_DATA:
            /*smaller eeproms can work given some SMBus extension calls */
            if (count > I2C_SMBUS_BLOCK_MAX)
                count = I2C_SMBUS_BLOCK_MAX;
            break;
        case I2C_SMBUS_WORD_DATA:
            /* Check for odd length transaction */
            count = (count == 1) ? 1 : 2;
            break;
        case I2C_SMBUS_BYTE_DATA:
            count = 1;
            break;
        default:
            /*
            * When we have a better choice than SMBus calls, use a
            * combined I2C message. Write address; then read up to
            * io_limit data bytes.  msgbuf is uint8_t and will cast to our
            * needs.
            */
            i = 0;
            msgbuf[i++] = offset;

            msg[0].addr = client->addr;
            msg[0].buf = msgbuf;
            msg[0].len = i;

            msg[1].addr = client->addr;
            msg[1].flags = I2C_M_RD;
            msg[1].buf = buf;
            msg[1].len = count;
    }

    /*
     * Reads fail if the previous write didn't complete yet. We may
     * loop a few times until this one succeeds, waiting at least
     * long enough for one entire page write to work.
     */
    timeout = jiffies + msecs_to_jiffies(write_timeout);
    do {
        read_time = jiffies;

        switch (optoe->use_smbus) {
            case I2C_SMBUS_I2C_BLOCK_DATA:
                status = i2c_smbus_read_i2c_block_data(client, offset, count, buf);
                break;
            case I2C_SMBUS_WORD_DATA:
                status = i2c_smbus_read_word_data(client, offset);
                if (status >= 0) {
                    buf[0] = status & 0xff;
                    if (count == 2)
                        buf[1] = status >> 8;
                    status = count;
                }
                break;
            case I2C_SMBUS_BYTE_DATA:
                status = i2c_smbus_read_byte_data(client, offset);
                if (status >= 0) {
                    buf[0] = status;
                    status = count;
                }
                break;
            default:
                status = i2c_transfer(client->adapter, msg, 2);
                if (status == 2)
                    status = count;
        }

        dev_dbg(&client->dev, "eeprom read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies);

        if (status == count) /* happy path */
            return count;

        if (status == -ENXIO) /* no module present */
            return status;

        /* REVISIT: at HZ=100, this is sloooow */
        usleep_range(1000, 2000);
    } while (time_before(read_time, timeout));

    return -ETIMEDOUT;
}

static ssize_t
optoe_eeprom_write(struct optoe_data *optoe, struct i2c_client *client, const char *buf, uint32_t offset, size_t count)
{
    struct i2c_msg msg;
    ssize_t status;
    size_t timeout, write_time;
    uint32_t next_page_start;
    int32_t i = 0;

    /* write max is at most a page
     * (In this driver, write_max is actually one byte!)
     */
    if (count > optoe->write_max)
        count = optoe->write_max;

    /* shorten count if necessary to avoid crossing page boundary */
    next_page_start = roundup(offset + 1, OPTOE_PAGE_SIZE);
    if (offset + count > next_page_start)
        count = next_page_start - offset;

    switch (optoe->use_smbus) {
        case I2C_SMBUS_I2C_BLOCK_DATA:
            /*smaller eeproms can work given some SMBus extension calls */
            if (count > I2C_SMBUS_BLOCK_MAX)
                count = I2C_SMBUS_BLOCK_MAX;
            break;
        case I2C_SMBUS_WORD_DATA:
            /* Check for odd length transaction */
            count = (count == 1) ? 1 : 2;
            break;
        case I2C_SMBUS_BYTE_DATA:
            count = 1;
            break;
        default:
            /* If we'll use I2C calls for I/O, set up the message */
            msg.addr = client->addr;
            msg.flags = 0;

            /* msg.buf is uint8_t and casts will mask the values */
            msg.buf = optoe->writebuf;

            msg.buf[i++] = offset;
            memcpy(&msg.buf[i], buf, count);
            msg.len = i + count;
            break;
    }

    /*
     * Reads fail if the previous write didn't complete yet. We may
     * loop a few times until this one succeeds, waiting at least
     * long enough for one entire page write to work.
     */
    timeout = jiffies + msecs_to_jiffies(write_timeout);
    do {
        write_time = jiffies;

        switch (optoe->use_smbus) {
            case I2C_SMBUS_I2C_BLOCK_DATA:
                status = i2c_smbus_write_i2c_block_data(client, offset, count, buf);
                if (status == 0)
                    status = count;
                break;
            case I2C_SMBUS_WORD_DATA:
                if (count == 2) {
                    status = i2c_smbus_write_word_data(client, offset, (uint16_t)((buf[0]) | (buf[1] << 8)));
                } else {
                    /* count = 1 */
                    status = i2c_smbus_write_byte_data(client, offset, buf[0]);
                }
                if (status == 0)
                    status = count;
                break;
            case I2C_SMBUS_BYTE_DATA:
                status = i2c_smbus_write_byte_data(client, offset, buf[0]);
                if (status == 0)
                    status = count;
                break;
            default:
                status = i2c_transfer(client->adapter, &msg, 1);
                if (status == 1)
                    status = count;
                break;
        }

        dev_dbg(&client->dev, "eeprom write %zu@%d --> %ld (%lu)\n", count, offset, (long int)status, jiffies);

        if (status == count)
            return count;

        /* REVISIT: at HZ=100, this is sloooow */
        usleep_range(1000, 2000);
    } while (time_before(write_time, timeout));

    return -ETIMEDOUT;
}

static ssize_t optoe_eeprom_update_client(struct optoe_data *optoe, char *buf, loff_t off, size_t count, int32_t opcode)
{
    struct i2c_client *client;
    ssize_t retval = 0;
    uint8_t page = 0;
    loff_t phy_offset = off;
    int32_t ret = 0;

    page = optoe_translate_offset(optoe, &phy_offset, &client);
    dev_dbg(
        &client->dev, "%s off %lld  page:%d phy_offset:%lld, count:%ld, opcode:%d\n", __func__, off, page, phy_offset,
        (long int)count, opcode);
    if (page > 0) {
        ret = optoe_eeprom_write(optoe, client, &page, OPTOE_PAGE_SELECT_REG, 1);
        if (ret < 0) {
            dev_dbg(&client->dev, "Write page register for page %d failed ret:%d!\n", page, ret);
            return ret;
        }
    }

    while (count) {
        ssize_t status;

        if (opcode == OPTOE_READ_OP) {
            status = optoe_eeprom_read(optoe, client, buf, phy_offset, count);
        } else {
            status = optoe_eeprom_write(optoe, client, buf, phy_offset, count);
        }
        if (status <= 0) {
            if (retval == 0)
                retval = status;
            break;
        }
        buf += status;
        phy_offset += status;
        count -= status;
        retval += status;
    }

    if (page > 0) {
        /* return the page register to page 0 (why?) */
        page = 0;
        ret = optoe_eeprom_write(optoe, client, &page, OPTOE_PAGE_SELECT_REG, 1);
        if (ret < 0) {
            dev_err(&client->dev, "Restore page register to 0 failed:%d!\n", ret);
            /* error only if nothing has been transferred */
            if (retval == 0)
                retval = ret;
        }
    }
    return retval;
}

/*
 * Figure out if this access is within the range of supported pages.
 * Note this is called on every access because we don't know if the
 * module has been replaced since the last call.
 * If/when modules support more pages, this is the routine to update
 * to validate and allow access to additional pages.
 *
 * Returns updated len for this access:
 *     - entire access is legal, original len is returned.
 *     - access begins legal but is too long, len is truncated to fit.
 *     - initial offset exceeds supported pages, return OPTOE_EOF (zero)
 */
static ssize_t optoe_page_legal(struct optoe_data *optoe, loff_t off, size_t len)
{
    struct i2c_client *client = optoe->client[0];
    uint8_t regval;
    int32_t not_pageable;
    int32_t status;
    size_t maxlen;

    if (off < 0)
        return -EINVAL;
    if (optoe->dev_class == TWO_ADDR) {
        /* SFP case */
        /* if only using addr 0x50 (first 256 bytes) we're good */
        if ((off + len) <= TWO_ADDR_NO_0X51_SIZE)
            return len;
        /* if offset exceeds possible pages, we're not good */
        if (off >= TWO_ADDR_EEPROM_SIZE)
            return OPTOE_EOF;
        /* in between, are pages supported? */
        status = optoe_eeprom_read(optoe, client, &regval, TWO_ADDR_PAGEABLE_REG, 1);
        if (status < 0)
            return status; /* error out (no module?) */
        if (regval & TWO_ADDR_PAGEABLE) {
            /* Pages supported, trim len to the end of pages */
            maxlen = TWO_ADDR_EEPROM_SIZE - off;
        } else {
            /* pages not supported, trim len to unpaged size */
            if (off >= TWO_ADDR_EEPROM_UNPAGED_SIZE)
                return OPTOE_EOF;

            /* will be accessing addr 0x51, is that supported? */
            /* byte 92, bit 6 implies DDM support, 0x51 support */
            status = optoe_eeprom_read(optoe, client, &regval, TWO_ADDR_0X51_REG, 1);
            if (status < 0)
                return status;
            if (regval & TWO_ADDR_0X51_SUPP) {
                /* addr 0x51 is OK */
                maxlen = TWO_ADDR_EEPROM_UNPAGED_SIZE - off;
            } else {
                /* addr 0x51 NOT supported, trim to 256 max */
                if (off >= TWO_ADDR_NO_0X51_SIZE)
                    return OPTOE_EOF;
                maxlen = TWO_ADDR_NO_0X51_SIZE - off;
            }
        }
        len = (len > maxlen) ? maxlen : len;
        dev_dbg(&client->dev, "page_legal, SFP, off %lld len %ld\n", off, (long int)len);
    } else {
        /* QSFP case, CMIS case */
        /* if no pages needed, we're good */
        if ((off + len) <= ONE_ADDR_EEPROM_UNPAGED_SIZE)
            return len;
        /* if offset exceeds possible pages, we're not good */
        if (off >= ONE_ADDR_EEPROM_SIZE)
            return OPTOE_EOF;
        /* in between, are pages supported? */
        status = optoe_eeprom_read(optoe, client, &regval, ONE_ADDR_PAGEABLE_REG, 1);
        if (status < 0)
            return status; /* error out (no module?) */

        if (optoe->dev_class == ONE_ADDR) {
            not_pageable = QSFP_NOT_PAGEABLE;
        } else {
            not_pageable = CMIS_NOT_PAGEABLE;
        }
        dev_dbg(&client->dev, "Paging Register: 0x%x; not_pageable mask: 0x%x\n", regval, not_pageable);

        if (regval & not_pageable) {
            /* pages not supported, trim len to unpaged size */
            if (off >= ONE_ADDR_EEPROM_UNPAGED_SIZE)
                return OPTOE_EOF;
            maxlen = ONE_ADDR_EEPROM_UNPAGED_SIZE - off;
        } else {
            /* Pages supported, trim len to the end of pages */
            maxlen = ONE_ADDR_EEPROM_SIZE - off;
        }
        len = (len > maxlen) ? maxlen : len;
        dev_dbg(&client->dev, "page_legal, QSFP, off %lld len %ld\n", off, (long int)len);
    }
    return len;
}

static ssize_t optoe_read_write(struct optoe_data *optoe, char *buf, loff_t off, size_t len, int32_t opcode)
{
    struct i2c_client *client = optoe->client[0];
    int32_t chunk;
    int32_t status = 0;
    ssize_t retval;
    size_t pending_len = 0, chunk_len = 0;
    loff_t chunk_offset = 0, chunk_start_offset = 0;
    loff_t chunk_end_offset = 0;

    dev_dbg(
        &client->dev, "%s: off %lld  len:%ld, opcode:%s\n", __func__, off, (long int)len,
        (opcode == OPTOE_READ_OP) ? "r" : "w");
    if (unlikely(!len))
        return len;

    /*
     * Read data from chip, protecting against concurrent updates
     * from this host, but not from other I2C masters.
     */
    mutex_lock(&optoe->lock);

    /*
     * Confirm this access fits within the device suppored addr range
     */
    status = optoe_page_legal(optoe, off, len);
    if ((status == OPTOE_EOF) || (status < 0)) {
        mutex_unlock(&optoe->lock);
        return status;
    }
    len = status;

    /*
     * For each (128 byte) chunk involved in this request, issue a
     * separate call to sff_eeprom_update_client(), to
     * ensure that each access recalculates the client/page
     * and writes the page register as needed.
     * Note that chunk to page mapping is confusing, is different for
     * QSFP and SFP, and never needs to be done.  Don't try!
     */
    pending_len = len; /* amount remaining to transfer */
    retval = 0;        /* amount transferred */
    for (chunk = off >> 7; chunk <= (off + len - 1) >> 7; chunk++) {
        /*
         * Compute the offset and number of bytes to be read/write
         *
         * 1. start at an offset not equal to 0 (within the chunk)
         *    and read/write less than the rest of the chunk
         * 2. start at an offset not equal to 0 and read/write the rest
         *    of the chunk
         * 3. start at offset 0 (within the chunk) and read/write less
         *    than entire chunk
         * 4. start at offset 0 (within the chunk), and read/write
         *    the entire chunk
         */
        chunk_start_offset = chunk * OPTOE_PAGE_SIZE;
        chunk_end_offset = chunk_start_offset + OPTOE_PAGE_SIZE;

        if (chunk_start_offset < off) {
            chunk_offset = off;
            if ((off + pending_len) < chunk_end_offset)
                chunk_len = pending_len;
            else
                chunk_len = chunk_end_offset - off;
        } else {
            chunk_offset = chunk_start_offset;
            if (pending_len < OPTOE_PAGE_SIZE)
                chunk_len = pending_len;
            else
                chunk_len = OPTOE_PAGE_SIZE;
        }

        dev_dbg(
            &client->dev,
            "sff_r/w: off %lld, len %ld, chunk_start_offset %lld, chunk_offset %lld, chunk_len %ld, pending_len %ld\n",
            off, (long int)len, chunk_start_offset, chunk_offset, (long int)chunk_len, (long int)pending_len);

        /*
         * note: chunk_offset is from the start of the EEPROM,
         * not the start of the chunk
         */
        status = optoe_eeprom_update_client(optoe, buf, chunk_offset, chunk_len, opcode);
        if (status != chunk_len) {
            /* This is another 'no device present' path */
            dev_dbg(
                &client->dev, "o_u_c: chunk %d c_offset %lld c_len %ld failed %d!\n", chunk, chunk_offset,
                (long int)chunk_len, status);
            if (status > 0)
                retval += status;
            if (retval == 0)
                retval = status;
            break;
        }
        buf += status;
        pending_len -= status;
        retval += status;
    }
    mutex_unlock(&optoe->lock);

    return retval;
}

static ssize_t
optoe_bin_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
    struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
    struct optoe_data *optoe = i2c_get_clientdata(client);

    if (!client || !optoe) {
        return -ENODEV;
    }

    return optoe_read_write(optoe, buf, off, count, OPTOE_READ_OP);
}

static ssize_t optoe_bin_write(
    struct file *filp,
    struct kobject *kobj,
    struct bin_attribute *attr,
    char *buf,
    loff_t off,
    size_t count)
{
    struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
    struct optoe_data *optoe = i2c_get_clientdata(client);

    return optoe_read_write(optoe, buf, off, count, OPTOE_WRITE_OP);
}

static int32_t optoe_remove(struct i2c_client *client)
{
    struct optoe_data *optoe;
    int32_t i;

    optoe = i2c_get_clientdata(client);
    sysfs_remove_group(&client->dev.kobj, &optoe->attr_group);
    sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin);

    for (i = 1; i < optoe->num_addresses; i++)
        i2c_unregister_device(optoe->client[i]);

#ifdef EEPROM_CLASS
    eeprom_device_unregister(optoe->eeprom_dev);
#endif

    kfree(optoe->writebuf);
    kfree(optoe);
    return 0;
}

static ssize_t show_dev_class(struct device *dev, struct device_attribute *dattr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    ssize_t count;

    if (!client || !optoe) {
        return -ENODEV;
    }

    mutex_lock(&optoe->lock);
    count = snprintf(buf, PAGE_SIZE, "%d\n", optoe->dev_class);
    mutex_unlock(&optoe->lock);

    return count;
}

static ssize_t set_dev_class(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    int32_t dev_class;

    if (!client || !optoe) {
        return -ENODEV;
    }

    /*
     * dev_class is actually the number of i2c addresses used, thus
     * legal values are "1" (QSFP class) and "2" (SFP class)
     * And...  CMIS spec is 1 i2c address, but puts the pageable
     * bit in a different location, so CMIS devices are "3"
     */

    if (kstrtoint(buf, 0, &dev_class) != 0 || dev_class < 1 || dev_class > 3)
        return -EINVAL;

    mutex_lock(&optoe->lock);
    if (dev_class == TWO_ADDR) {
        /* SFP family */
        /* if it doesn't exist, create 0x51 i2c address */
        if (!optoe->client[1]) {
            optoe->client[1] = i2c_new_dummy(client->adapter, 0x51);
            if (!optoe->client[1]) {
                dev_err(&client->dev, "address 0x51 unavailable\n");
                mutex_unlock(&optoe->lock);
                return -EADDRINUSE;
            }
        }
        optoe->bin.size = TWO_ADDR_EEPROM_SIZE;
        optoe->num_addresses = 2;
    } else {
        /* one-address (eg QSFP) and CMIS family */
        /* if it exists, remove 0x51 i2c address */
        if (optoe->client[1])
            i2c_unregister_device(optoe->client[1]);
        optoe->bin.size = ONE_ADDR_EEPROM_SIZE;
        optoe->num_addresses = 1;
    }
    optoe->dev_class = dev_class;
    mutex_unlock(&optoe->lock);

    return count;
}
static DEVICE_ATTR(dev_class, 0644, show_dev_class, set_dev_class);

/*
 * if using the EEPROM CLASS driver, we don't report a port_name,
 * the EEPROM CLASS drive handles that.  Hence all this code is
 * only compiled if we are NOT using the EEPROM CLASS driver.
 */
#ifndef EEPROM_CLASS

static ssize_t show_port_name(struct device *dev, struct device_attribute *dattr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    ssize_t count;

    if (!client || !optoe)
        return -ENODEV;

    mutex_lock(&optoe->lock);
    count = snprintf(buf, MAX_PORT_NAME_LEN, "%s\n", optoe->port_name);
    mutex_unlock(&optoe->lock);

    return count;
}

static ssize_t set_port_name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    char port_name[MAX_PORT_NAME_LEN];

    /* no checking, this value is not used except by show_port_name */

    if (sscanf(buf, "%19s", port_name) != 1)
        return -EINVAL;

    mutex_lock(&optoe->lock);
    strncpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN);
    mutex_unlock(&optoe->lock);

    return count;
}
static DEVICE_ATTR(port_name, 0644, show_port_name, set_port_name);
#endif /* if NOT defined EEPROM_CLASS, the common case */

static struct attribute *optoe_attrs[] = {
#ifndef EEPROM_CLASS
    &dev_attr_port_name.attr,
#endif
    &dev_attr_dev_class.attr,
    NULL,
};

static struct attribute_group optoe_attr_group = {
    .attrs = optoe_attrs,
};

static ssize_t default_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    struct i2c_client *client = kobj_to_i2c_client(kobj->parent);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_kobj_attr_st *dev_attr;
    ssize_t retval;
    char val, val_mask, reg_val;
    const size_t len = 1; /* read 1 byte */

    if (!client || !optoe) {
        return -ENODEV;
    }

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    if (!i2c_attr)
        return -ENODEV;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr->ida_show) {
        return -EINVAL;
    }

    optoe->use_smbus = I2C_SMBUS_BYTE_DATA;
    retval = optoe_read_write(optoe, &reg_val, dev_attr->ida_reg, len, OPTOE_READ_OP);
    if (retval != len) {
        return -EINVAL;
    }
    val_mask = (1 << (dev_attr->ida_n_bits)) - 1;
    val = (reg_val >> dev_attr->ida_bit_offset) & val_mask;

    return snprintf(buf, PAGE_SIZE, "%#x\n", val);
}

static ssize_t default_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    struct i2c_client *client = kobj_to_i2c_client(kobj->parent);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_kobj_attr_st *dev_attr;
    ssize_t retval;
    char val;
    size_t req_val;
    size_t req_val_mask;
    const size_t len = 1; /* read 1 byte */

    if (!client || !optoe) {
        return -ENODEV;
    }

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    if (!i2c_attr)
        return -ENODEV;

    dev_attr = i2c_attr->isa_i2c_attr;
    if (!dev_attr)
        return -ENODEV;

    retval = kstrtoul(buf, 0, &req_val);
    if (retval < 0) {
        return -EINVAL;
    }

    req_val_mask = ~(((-1) >> (dev_attr->ida_n_bits)) << (dev_attr->ida_n_bits));
    req_val &= req_val_mask;

    optoe->use_smbus = I2C_SMBUS_BYTE_DATA;
    retval = optoe_read_write(optoe, &val, dev_attr->ida_reg, len, OPTOE_READ_OP);
    if (retval != len) {
        return -EINVAL;
    }

    val &= ~(req_val_mask << dev_attr->ida_bit_offset);
    val |= req_val << dev_attr->ida_bit_offset;

    retval = optoe_read_write(optoe, &val, dev_attr->ida_reg, len, OPTOE_WRITE_OP);
    if (retval != len) {
        return -EINVAL;
    }

    return count;
}

static ssize_t freq_span_range_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    struct i2c_client *client = kobj_to_i2c_client(kobj->parent);
    struct optoe_data *optoe = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_kobj_attr_st *dev_attr;
    ssize_t retval;
    size_t value = 0;
    char buffer[HEX2DEC_2_BYTE_LEN] = {};
    char reg_val[2] = {}; /* read 2 byte */
    const size_t len = 2; /* read 2 byte */

    if (!client || !optoe) {
        return -ENODEV;
    }

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);
    if (!i2c_attr)
        return -ENODEV;

    dev_attr = i2c_attr->isa_i2c_attr;
    if (!dev_attr)
        return -ENODEV;

    optoe->use_smbus = I2C_SMBUS_BYTE_DATA;
    retval = optoe_read_write(optoe, reg_val, dev_attr->ida_reg, len, OPTOE_READ_OP);
    if (retval != len) {
        return -EINVAL;
    }

    retval = snprintf(buffer, HEX2DEC_2_BYTE_LEN, "0x%x%x\n", reg_val[0], reg_val[1]);
    if (retval == 0) {
        return -EINVAL;
    }

    retval = kstrtoul(buffer, 0, &value);
    if (retval < 0) {
        return -EINVAL;
    }

    return snprintf(buf, PAGE_SIZE, "%zu\n", value);
}

static const i2c_kobj_attr_st optoe_attr_table[] = {
    {
        "low_power",
        NULL,
        default_show,
        default_store,
        26, /* 0x00 * OPTOE_PAGE_SIZE + 26 */
        6,  /* offset */
        1,  /* length */
    },
    {
        "freq_span_range_3_125_low",
        NULL,
        freq_span_range_show,
        NULL,
        0x04 * OPTOE_PAGE_SIZE + 130,
        0,
        16,
    },
    {
        "channel_number_lan1",
        NULL,
        default_show,
        default_store,
        0x12 * OPTOE_PAGE_SIZE + 128,
        4,
        4,
    }};

static i2c_kobj_data_st optoe_data;

static int32_t optoe_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int32_t err;
    int32_t use_smbus = 0;
    struct optoe_platform_data chip;
    struct optoe_data *optoe;
    int32_t num_addresses = 0;
    char port_name[MAX_PORT_NAME_LEN];
    int n_attrs = sizeof(optoe_attr_table) / sizeof(optoe_attr_table[0]);

    if (client->addr != 0x50) {
        dev_dbg(&client->dev, "probe, bad i2c addr: 0x%x\n", client->addr);
        err = -EINVAL;
        goto exit;
    }

    if (client->dev.platform_data) {
        chip = *(struct optoe_platform_data *)client->dev.platform_data;
        /* take the port name from the supplied platform data */
#ifdef EEPROM_CLASS
        strncpy(port_name, chip.eeprom_data->label, MAX_PORT_NAME_LEN);
#else
        memcpy(port_name, chip.port_name, MAX_PORT_NAME_LEN);
#endif
        dev_dbg(&client->dev, "probe, chip provided, flags:0x%x; name: %s\n", chip.flags, client->name);
    } else {
        if (!id->driver_data) {
            err = -ENODEV;
            goto exit;
        }
        dev_dbg(&client->dev, "probe, building chip\n");
        strncpy(port_name, "unitialized", 12);
        chip.flags = 0;
#ifdef EEPROM_CLASS
        chip.eeprom_data = NULL;
#endif
    }

    /* Use I2C operations unless we're stuck with SMBus extensions. */
    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
        if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
            use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
        } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
            use_smbus = I2C_SMBUS_WORD_DATA;
        } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
            use_smbus = I2C_SMBUS_BYTE_DATA;
        } else {
            err = -EPFNOSUPPORT;
            goto exit;
        }
    }

    /*
     * Make room for two i2c clients
     */
    num_addresses = 2;

    optoe = kzalloc(sizeof(struct optoe_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
    if (!optoe) {
        err = -ENOMEM;
        goto exit;
    }

    mutex_init(&optoe->lock);

    /* determine whether this is a one-address or two-address module */
    if ((strcmp(client->name, "optoe1") == 0) || (strcmp(client->name, "sff8436") == 0)) {
        /* one-address (eg QSFP) family */
        optoe->dev_class = ONE_ADDR;
        chip.byte_len = ONE_ADDR_EEPROM_SIZE;
        num_addresses = 1;
    } else if ((strcmp(client->name, "optoe2") == 0) || (strcmp(client->name, "24c04") == 0)) {
        /* SFP family */
        optoe->dev_class = TWO_ADDR;
        chip.byte_len = TWO_ADDR_EEPROM_SIZE;
        num_addresses = 2;
    } else if (strcmp(client->name, "optoe3") == 0) {
        /* CMIS spec */
        optoe->dev_class = CMIS_ADDR;
        chip.byte_len = ONE_ADDR_EEPROM_SIZE;
        num_addresses = 1;
    } else { /* those were the only choices */
        err = -EINVAL;
        goto exit_kfree;
    }

    dev_dbg(&client->dev, "dev_class: %d\n", optoe->dev_class);
    optoe->use_smbus = use_smbus;
    optoe->chip = chip;
    optoe->num_addresses = num_addresses;
    memcpy(optoe->port_name, port_name, MAX_PORT_NAME_LEN);

    /*
     * Export the EEPROM bytes through sysfs, since that's convenient.
     * By default, only root should see the data (maybe passwords etc)
     */
    sysfs_bin_attr_init(&optoe->bin);
    optoe->bin.attr.name = "eeprom";
    optoe->bin.attr.mode = 0444;
    optoe->bin.read = optoe_bin_read;
    optoe->bin.size = chip.byte_len;

    if (!use_smbus || (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
        /*
         * NOTE: AN-2079
         * Finisar recommends that the host implement 1 byte writes
         * only since this module only supports 32 byte page boundaries.
         * 2 byte writes are acceptable for PE and Vout changes per
         * Application Note AN-2071.
         */
        uint32_t write_max = 1;

        optoe->bin.write = optoe_bin_write;
        optoe->bin.attr.mode |= 0200;

        if (write_max > io_limit)
            write_max = io_limit;
        if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
            write_max = I2C_SMBUS_BLOCK_MAX;
        optoe->write_max = write_max;

        /* buffer (data + address at the beginning) */
        optoe->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
        if (!optoe->writebuf) {
            err = -ENOMEM;
            goto exit_kfree;
        }
    } else {
        dev_warn(&client->dev, "cannot write due to controller restrictions.");
    }

    optoe->client[0] = client;

    /* SFF-8472 spec requires that the second I2C address be 0x51 */
    if (num_addresses == 2) {
        optoe->client[1] = i2c_new_dummy(client->adapter, 0x51);
        if (!optoe->client[1]) {
            dev_err(&client->dev, "address 0x51 unavailable\n");
            err = -EADDRINUSE;
            goto err_struct;
        }
    }

    /* create the sysfs eeprom file */
    err = sysfs_create_bin_file(&client->dev.kobj, &optoe->bin);
    if (err)
        goto err_struct;

    optoe->attr_group = optoe_attr_group;

    err = sysfs_create_group(&client->dev.kobj, &optoe->attr_group);
    if (err) {
        dev_err(&client->dev, "failed to create sysfs attribute group.\n");
        goto err_struct;
    }

    i2c_kobj_sysfs_data_complex_init(client, &optoe_data, optoe_attr_table, n_attrs, NODE_DIR);

#ifdef EEPROM_CLASS
    optoe->eeprom_dev = eeprom_device_register(&client->dev, chip.eeprom_data);
    if (IS_ERR(optoe->eeprom_dev)) {
        dev_err(&client->dev, "error registering eeprom device.\n");
        err = PTR_ERR(optoe->eeprom_dev);
        goto err_sysfs_cleanup;
    }
#endif

    i2c_set_clientdata(client, optoe);

    dev_info(
        &client->dev, "%zu byte %s EEPROM, %s\n", optoe->bin.size, client->name,
        optoe->bin.write ? "read/write" : "read-only");

    if (use_smbus == I2C_SMBUS_WORD_DATA || use_smbus == I2C_SMBUS_BYTE_DATA) {
        dev_notice(
            &client->dev, "Falling back to %s reads, performance will suffer\n",
            use_smbus == I2C_SMBUS_WORD_DATA ? "word" : "byte");
    }

    return 0;

#ifdef EEPROM_CLASS
err_sysfs_cleanup:
    sysfs_remove_group(&client->dev.kobj, &optoe->attr_group);
    sysfs_remove_bin_file(&client->dev.kobj, &optoe->bin);
#endif

err_struct:
    if (num_addresses == 2) {
        if (optoe->client[1])
            i2c_unregister_device(optoe->client[1]);
    }

    kfree(optoe->writebuf);
exit_kfree:
    kfree(optoe);
exit:
    dev_dbg(&client->dev, "probe error %d\n", err);

    return err;
}

/*-------------------------------------------------------------------------*/

static struct i2c_driver optoe_driver = {
    .driver = {
        .name = "optoe",
        .owner = THIS_MODULE,
    },
    .probe = optoe_probe,
    .remove = optoe_remove,
    .id_table = optoe_ids,
};

static int32_t __init optoe_init(void)
{
    if (!io_limit) {
        pr_err("optoe: io_limit must not be 0!\n");
        return -EINVAL;
    }

    io_limit = rounddown_pow_of_two(io_limit);
    return i2c_add_driver(&optoe_driver);
}
module_init(optoe_init);

static void __exit optoe_exit(void)
{
    i2c_del_driver(&optoe_driver);
}
module_exit(optoe_exit);

MODULE_DESCRIPTION("Driver for optical transceiver (SFP, QSFP, ...) EEPROMs");
MODULE_AUTHOR("DON BOLLINGER ");
MODULE_LICENSE("GPL");

pmbus.h

/*
 * pmbus.h - Common defines and structures for PMBus devices
 *
 * Copyright (c) 2010, 2011 Ericsson AB.
 * Copyright (c) 2012 Guenter Roeck
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef PMBUS_H
#define PMBUS_H

#include 
#include 

/*
 * Registers
 */
enum pmbus_regs
{
    PMBUS_PAGE = 0x00,
    PMBUS_OPERATION = 0x01,
    PMBUS_ON_OFF_CONFIG = 0x02,
    PMBUS_CLEAR_FAULTS = 0x03,
    PMBUS_PHASE = 0x04,

    PMBUS_CAPABILITY = 0x19,
    PMBUS_QUERY = 0x1A,

    PMBUS_VOUT_MODE = 0x20,
    PMBUS_VOUT_COMMAND = 0x21,
    PMBUS_VOUT_TRIM = 0x22,
    PMBUS_VOUT_CAL_OFFSET = 0x23,
    PMBUS_VOUT_MAX = 0x24,
    PMBUS_VOUT_MARGIN_HIGH = 0x25,
    PMBUS_VOUT_MARGIN_LOW = 0x26,
    PMBUS_VOUT_TRANSITION_RATE = 0x27,
    PMBUS_VOUT_DROOP = 0x28,
    PMBUS_VOUT_SCALE_LOOP = 0x29,
    PMBUS_VOUT_SCALE_MONITOR = 0x2A,

    PMBUS_COEFFICIENTS = 0x30,
    PMBUS_POUT_MAX = 0x31,

    PMBUS_FAN_CONFIG_12 = 0x3A,
    PMBUS_FAN_COMMAND_1 = 0x3B,
    PMBUS_FAN_COMMAND_2 = 0x3C,
    PMBUS_FAN_CONFIG_34 = 0x3D,
    PMBUS_FAN_COMMAND_3 = 0x3E,
    PMBUS_FAN_COMMAND_4 = 0x3F,

    PMBUS_VOUT_OV_FAULT_LIMIT = 0x40,
    PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41,
    PMBUS_VOUT_OV_WARN_LIMIT = 0x42,
    PMBUS_VOUT_UV_WARN_LIMIT = 0x43,
    PMBUS_VOUT_UV_FAULT_LIMIT = 0x44,
    PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45,
    PMBUS_IOUT_OC_FAULT_LIMIT = 0x46,
    PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47,
    PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48,
    PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49,
    PMBUS_IOUT_OC_WARN_LIMIT = 0x4A,
    PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B,
    PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C,

    PMBUS_OT_FAULT_LIMIT = 0x4F,
    PMBUS_OT_FAULT_RESPONSE = 0x50,
    PMBUS_OT_WARN_LIMIT = 0x51,
    PMBUS_UT_WARN_LIMIT = 0x52,
    PMBUS_UT_FAULT_LIMIT = 0x53,
    PMBUS_UT_FAULT_RESPONSE = 0x54,
    PMBUS_VIN_OV_FAULT_LIMIT = 0x55,
    PMBUS_VIN_OV_FAULT_RESPONSE = 0x56,
    PMBUS_VIN_OV_WARN_LIMIT = 0x57,
    PMBUS_VIN_UV_WARN_LIMIT = 0x58,
    PMBUS_VIN_UV_FAULT_LIMIT = 0x59,

    PMBUS_IIN_OC_FAULT_LIMIT = 0x5B,
    PMBUS_IIN_OC_WARN_LIMIT = 0x5D,

    PMBUS_POUT_OP_FAULT_LIMIT = 0x68,
    PMBUS_POUT_OP_WARN_LIMIT = 0x6A,
    PMBUS_PIN_OP_WARN_LIMIT = 0x6B,

    PMBUS_STATUS_BYTE = 0x78,
    PMBUS_STATUS_WORD = 0x79,
    PMBUS_STATUS_VOUT = 0x7A,
    PMBUS_STATUS_IOUT = 0x7B,
    PMBUS_STATUS_INPUT = 0x7C,
    PMBUS_STATUS_TEMPERATURE = 0x7D,
    PMBUS_STATUS_CML = 0x7E,
    PMBUS_STATUS_OTHER = 0x7F,
    PMBUS_STATUS_MFR_SPECIFIC = 0x80,
    PMBUS_STATUS_FAN_12 = 0x81,
    PMBUS_STATUS_FAN_34 = 0x82,

    PMBUS_READ_VIN = 0x88,
    PMBUS_READ_IIN = 0x89,
    PMBUS_READ_VCAP = 0x8A,
    PMBUS_READ_VOUT = 0x8B,
    PMBUS_READ_IOUT = 0x8C,
    PMBUS_READ_TEMPERATURE_1 = 0x8D,
    PMBUS_READ_TEMPERATURE_2 = 0x8E,
    PMBUS_READ_TEMPERATURE_3 = 0x8F,
    PMBUS_READ_FAN_SPEED_1 = 0x90,
    PMBUS_READ_FAN_SPEED_2 = 0x91,
    PMBUS_READ_FAN_SPEED_3 = 0x92,
    PMBUS_READ_FAN_SPEED_4 = 0x93,
    PMBUS_READ_DUTY_CYCLE = 0x94,
    PMBUS_READ_FREQUENCY = 0x95,
    PMBUS_READ_POUT = 0x96,
    PMBUS_READ_PIN = 0x97,

    PMBUS_REVISION = 0x98,
    PMBUS_MFR_ID = 0x99,
    PMBUS_MFR_MODEL = 0x9A,
    PMBUS_MFR_REVISION = 0x9B,
    PMBUS_MFR_LOCATION = 0x9C,
    PMBUS_MFR_DATE = 0x9D,
    PMBUS_MFR_SERIAL = 0x9E,

    /*
 * Virtual registers.
 * Useful to support attributes which are not supported by standard PMBus
 * registers but exist as manufacturer specific registers on individual chips.
 * Must be mapped to real registers in device specific code.
 *
 * Semantics:
 * Virtual registers are all word size.
 * READ registers are read-only; writes are either ignored or return an error.
 * RESET registers are read/write. Reading reset registers returns zero
 * (used for detection), writing any value causes the associated history to be
 * reset.
 * Virtual registers have to be handled in device specific driver code. Chip
 * driver code returns non-negative register values if a virtual register is
 * supported, or a negative error code if not. The chip driver may return
 * -ENODATA or any other error code in this case, though an error code other
 * than -ENODATA is handled more efficiently and thus preferred. Either case,
 * the calling PMBus core code will abort if the chip driver returns an error
 * code when reading or writing virtual registers.
 */
    PMBUS_VIRT_BASE = 0x100,
    PMBUS_VIRT_READ_TEMP_AVG,
    PMBUS_VIRT_READ_TEMP_MIN,
    PMBUS_VIRT_READ_TEMP_MAX,
    PMBUS_VIRT_RESET_TEMP_HISTORY,
    PMBUS_VIRT_READ_VIN_AVG,
    PMBUS_VIRT_READ_VIN_MIN,
    PMBUS_VIRT_READ_VIN_MAX,
    PMBUS_VIRT_RESET_VIN_HISTORY,
    PMBUS_VIRT_READ_IIN_AVG,
    PMBUS_VIRT_READ_IIN_MIN,
    PMBUS_VIRT_READ_IIN_MAX,
    PMBUS_VIRT_RESET_IIN_HISTORY,
    PMBUS_VIRT_READ_PIN_AVG,
    PMBUS_VIRT_READ_PIN_MIN,
    PMBUS_VIRT_READ_PIN_MAX,
    PMBUS_VIRT_RESET_PIN_HISTORY,
    PMBUS_VIRT_READ_POUT_AVG,
    PMBUS_VIRT_READ_POUT_MIN,
    PMBUS_VIRT_READ_POUT_MAX,
    PMBUS_VIRT_RESET_POUT_HISTORY,
    PMBUS_VIRT_READ_VOUT_AVG,
    PMBUS_VIRT_READ_VOUT_MIN,
    PMBUS_VIRT_READ_VOUT_MAX,
    PMBUS_VIRT_RESET_VOUT_HISTORY,
    PMBUS_VIRT_READ_IOUT_AVG,
    PMBUS_VIRT_READ_IOUT_MIN,
    PMBUS_VIRT_READ_IOUT_MAX,
    PMBUS_VIRT_RESET_IOUT_HISTORY,
    PMBUS_VIRT_READ_TEMP2_AVG,
    PMBUS_VIRT_READ_TEMP2_MIN,
    PMBUS_VIRT_READ_TEMP2_MAX,
    PMBUS_VIRT_RESET_TEMP2_HISTORY,

    PMBUS_VIRT_READ_VMON,
    PMBUS_VIRT_VMON_UV_WARN_LIMIT,
    PMBUS_VIRT_VMON_OV_WARN_LIMIT,
    PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
    PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
    PMBUS_VIRT_STATUS_VMON,
};

/*
 * OPERATION
 */
#define PB_OPERATION_CONTROL_ON      BIT(7)

/*
 * CAPABILITY
 */
#define PB_CAPABILITY_SMBALERT       BIT(4)
#define PB_CAPABILITY_ERROR_CHECK    BIT(7)

/*
 * VOUT_MODE
 */
#define PB_VOUT_MODE_MODE_MASK       0xe0
#define PB_VOUT_MODE_PARAM_MASK      0x1f

#define PB_VOUT_MODE_LINEAR          0x00
#define PB_VOUT_MODE_VID             0x20
#define PB_VOUT_MODE_DIRECT          0x40

/*
 * Fan configuration
 */
#define PB_FAN_2_PULSE_MASK          (BIT(0) | BIT(1))
#define PB_FAN_2_RPM                 BIT(2)
#define PB_FAN_2_INSTALLED           BIT(3)
#define PB_FAN_1_PULSE_MASK          (BIT(4) | BIT(5))
#define PB_FAN_1_RPM                 BIT(6)
#define PB_FAN_1_INSTALLED           BIT(7)

/*
 * STATUS_BYTE, STATUS_WORD (lower)
 */
#define PB_STATUS_NONE_ABOVE         BIT(0)
#define PB_STATUS_CML                BIT(1)
#define PB_STATUS_TEMPERATURE        BIT(2)
#define PB_STATUS_VIN_UV             BIT(3)
#define PB_STATUS_IOUT_OC            BIT(4)
#define PB_STATUS_VOUT_OV            BIT(5)
#define PB_STATUS_OFF                BIT(6)
#define PB_STATUS_BUSY               BIT(7)

/*
 * STATUS_WORD (upper)
 */
#define PB_STATUS_UNKNOWN            BIT(8)
#define PB_STATUS_OTHER              BIT(9)
#define PB_STATUS_FANS               BIT(10)
#define PB_STATUS_POWER_GOOD_N       BIT(11)
#define PB_STATUS_WORD_MFR           BIT(12)
#define PB_STATUS_INPUT              BIT(13)
#define PB_STATUS_IOUT_POUT          BIT(14)
#define PB_STATUS_VOUT               BIT(15)

/*
 * STATUS_IOUT
 */
#define PB_POUT_OP_WARNING           BIT(0)
#define PB_POUT_OP_FAULT             BIT(1)
#define PB_POWER_LIMITING            BIT(2)
#define PB_CURRENT_SHARE_FAULT       BIT(3)
#define PB_IOUT_UC_FAULT             BIT(4)
#define PB_IOUT_OC_WARNING           BIT(5)
#define PB_IOUT_OC_LV_FAULT          BIT(6)
#define PB_IOUT_OC_FAULT             BIT(7)

/*
 * STATUS_VOUT, STATUS_INPUT
 */
#define PB_VOLTAGE_UV_FAULT          BIT(4)
#define PB_VOLTAGE_UV_WARNING        BIT(5)
#define PB_VOLTAGE_OV_WARNING        BIT(6)
#define PB_VOLTAGE_OV_FAULT          BIT(7)

/*
 * STATUS_INPUT
 */
#define PB_PIN_OP_WARNING            BIT(0)
#define PB_IIN_OC_WARNING            BIT(1)
#define PB_IIN_OC_FAULT              BIT(2)

/*
 * STATUS_TEMPERATURE
 */
#define PB_TEMP_UT_FAULT             BIT(4)
#define PB_TEMP_UT_WARNING           BIT(5)
#define PB_TEMP_OT_WARNING           BIT(6)
#define PB_TEMP_OT_FAULT             BIT(7)

/*
 * STATUS_FAN
 */
#define PB_FAN_AIRFLOW_WARNING       BIT(0)
#define PB_FAN_AIRFLOW_FAULT         BIT(1)
#define PB_FAN_FAN2_SPEED_OVERRIDE   BIT(2)
#define PB_FAN_FAN1_SPEED_OVERRIDE   BIT(3)
#define PB_FAN_FAN2_WARNING          BIT(4)
#define PB_FAN_FAN1_WARNING          BIT(5)
#define PB_FAN_FAN2_FAULT            BIT(6)
#define PB_FAN_FAN1_FAULT            BIT(7)

/*
 * CML_FAULT_STATUS
 */
#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0)
#define PB_CML_FAULT_OTHER_COMM      BIT(1)
#define PB_CML_FAULT_PROCESSOR       BIT(3)
#define PB_CML_FAULT_MEMORY          BIT(4)
#define PB_CML_FAULT_PACKET_ERROR    BIT(5)
#define PB_CML_FAULT_INVALID_DATA    BIT(6)
#define PB_CML_FAULT_INVALID_COMMAND BIT(7)

enum pmbus_sensor_classes
{
    PSC_VOLTAGE_IN = 0,
    PSC_VOLTAGE_OUT,
    PSC_CURRENT_IN,
    PSC_CURRENT_OUT,
    PSC_POWER,
    PSC_TEMPERATURE,
    PSC_FAN,
    PSC_NUM_CLASSES /* Number of power sensor classes */
};

#define PMBUS_PAGES             32 /* Per PMBus specification */

/* Functionality bit mask */
#define PMBUS_HAVE_VIN          BIT(0)
#define PMBUS_HAVE_VCAP         BIT(1)
#define PMBUS_HAVE_VOUT         BIT(2)
#define PMBUS_HAVE_IIN          BIT(3)
#define PMBUS_HAVE_IOUT         BIT(4)
#define PMBUS_HAVE_PIN          BIT(5)
#define PMBUS_HAVE_POUT         BIT(6)
#define PMBUS_HAVE_FAN12        BIT(7)
#define PMBUS_HAVE_FAN34        BIT(8)
#define PMBUS_HAVE_TEMP         BIT(9)
#define PMBUS_HAVE_TEMP2        BIT(10)
#define PMBUS_HAVE_TEMP3        BIT(11)
#define PMBUS_HAVE_STATUS_VOUT  BIT(12)
#define PMBUS_HAVE_STATUS_IOUT  BIT(13)
#define PMBUS_HAVE_STATUS_INPUT BIT(14)
#define PMBUS_HAVE_STATUS_TEMP  BIT(15)
#define PMBUS_HAVE_STATUS_FAN12 BIT(16)
#define PMBUS_HAVE_STATUS_FAN34 BIT(17)
#define PMBUS_HAVE_VMON         BIT(18)
#define PMBUS_HAVE_STATUS_VMON  BIT(19)

enum pmbus_data_format
{
    linear = 0,
    direct,
    vid
};
enum vrm_version
{
    vr11 = 0,
    vr12,
    vr13,
    imvp9,
    amd625mv
};

struct pmbus_driver_info {
    int pages; /* Total number of pages */
    enum pmbus_data_format format[PSC_NUM_CLASSES];
    enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */
    /*
	 * Support one set of coefficients for each sensor type
	 * Used for chips providing data in direct mode.
	 */
    int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */
    int b[PSC_NUM_CLASSES]; /* offset */
    int R[PSC_NUM_CLASSES]; /* exponent */

    u32 func[PMBUS_PAGES]; /* Functionality, per page */
    /*
	 * The following functions map manufacturing specific register values
	 * to PMBus standard register values. Specify only if mapping is
	 * necessary.
	 * Functions return the register value (read) or zero (write) if
	 * successful. A return value of -ENODATA indicates that there is no
	 * manufacturer specific register, but that a standard PMBus register
	 * may exist. Any other negative return value indicates that the
	 * register does not exist, and that no attempt should be made to read
	 * the standard register.
	 */
    int (*read_byte_data)(struct i2c_client *client, int page, int reg);
    int (*read_word_data)(struct i2c_client *client, int page, int reg);
    int (*write_word_data)(struct i2c_client *client, int page, int reg, u16 word);
    int (*write_byte)(struct i2c_client *client, int page, u8 value);
    /*
	 * The identify function determines supported PMBus functionality.
	 * This function is only necessary if a chip driver supports multiple
	 * chips, and the chip functionality is not pre-determined.
	 */
    int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info);

    /* Regulator functionality, if supported by this chip driver. */
    int num_regulators;
    const struct regulator_desc *reg_desc;
};

/* Regulator ops */

extern const struct regulator_ops pmbus_regulator_ops;

/* Macro for filling in array of struct regulator_desc */
#define PMBUS_REGULATOR(_name, _id)                    \
    [_id] = {                                          \
        .name = (_name #_id),                          \
        .id = (_id),                                   \
        .of_match = of_match_ptr(_name #_id),          \
        .regulators_node = of_match_ptr("regulators"), \
        .ops = &pmbus_regulator_ops,                   \
        .type = REGULATOR_VOLTAGE,                     \
        .owner = THIS_MODULE,                          \
    }

/* Function declarations */

void pmbus_clear_cache(struct i2c_client *client);
int pmbus_set_page(struct i2c_client *client, u8 page);
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value);
int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info);
int pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
                                                          *client);

#endif /* PMBUS_H */

pxe1410.c

/*
 * Copyright 2019-present Facebook. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include 
#include 
#include 
#include 
#include 
#include "i2c_dev_sysfs.h"

#include "pmbus.h"

#define MAX_RETRY_COUNT 10

/*
 * Data Format
 * X = Y*2^N
 * X is the "real world" value;
 * Y(mantissa) = bit[0:10]
 * N(exponent) = bit[11:15], bit 15 is sign bit
 */
static int32_t linear_convert(int32_t data)
{
    int16_t exponent;
    int32_t mantissa;
    int32_t val_x;

    mantissa = (((data & 0x7ff) << 5)) >> 5;
    exponent = ((int16_t)data) >> 11;

    val_x = mantissa * 1000L;

    if (exponent >= 0)
        val_x <<= exponent;
    else
        val_x >>= -exponent;

    return val_x;
}

static int32_t i2c_set_page(struct i2c_client *client, int32_t page)
{
    int32_t rv;
    rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
    if (rv == 0) {
        rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
        if (rv < 0)
            return rv;

        if (rv != page)
            return -EIO;
        else
            return 0;
    }
    return rv;
}

static ssize_t chip_vout_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = 500 + (result - 1) * 10;

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_vin_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_pin_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_pout_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result) * 1000;

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_iout_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_iin_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_limit_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static ssize_t chip_temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct i2c_client *client = to_i2c_client(dev);
    i2c_dev_data_st *data = i2c_get_clientdata(client);
    i2c_sysfs_attr_st *i2c_attr;
    const i2c_dev_attr_st *dev_attr;
    int32_t result = -1;
    int32_t i = 0;

    if (!data)
        return -EINVAL;

    i2c_attr = TO_I2C_SYSFS_ATTR(attr);

    if (!i2c_attr)
        return -EINVAL;

    dev_attr = i2c_attr->isa_i2c_attr;

    if (!dev_attr)
        return -EINVAL;

    mutex_lock(&data->idd_lock);
    while ((result < 0) && (i < MAX_RETRY_COUNT)) {
        result = i2c_set_page(client, 0);
        if (result == 0)
            result = i2c_smbus_read_word_data(client, (dev_attr->ida_reg));
        i++;
    }
    mutex_unlock(&data->idd_lock);

    if (result >= 0)
        result = linear_convert(result);

    return scnprintf(buf, PAGE_SIZE, "%d\n", result);
}

static const i2c_dev_attr_st chip_attr_table[] = {
    {
        "in1_input", //READ_VIN
        NULL,
        chip_vin_show,
        NULL,
        0x88,
        0,
        8,
    },
    {
        "in2_input", //READ_VOUT
        NULL,
        chip_vout_show,
        NULL,
        0x8b,
        0,
        8,
    },
    {
        "curr1_input", //READ_IIN
        NULL,
        chip_iin_show,
        NULL,
        0x89,
        0,
        8,
    },
    {
        "curr2_input", //READ_IOUT
        NULL,
        chip_iout_show,
        NULL,
        0x8c,
        0,
        8,
    },
    {
        "power1_input", //READ_PIN
        NULL,
        chip_pin_show,
        NULL,
        0x97,
        0,
        8,
    },
    {
        "power2_input", //READ_POUT
        NULL,
        chip_pout_show,
        NULL,
        0x96,
        0,
        8,
    },
    {
        "temp1_input", //READ_TEMPERATURE_1
        NULL,
        chip_temp_show,
        NULL,
        0x8d,
        0,
        8,
    },
    {
        "in1_max",
        NULL,
        chip_limit_show,
        NULL,
        0x55,
        0,
        8,
    },
    {
        "in1_min",
        NULL,
        chip_limit_show,
        NULL,
        0x59,
        0,
        8,
    },
    {
        "in2_max",
        NULL,
        chip_limit_show,
        NULL,
        0x40,
        0,
        8,
    },
    {
        "in2_min",
        NULL,
        chip_limit_show,
        NULL,
        0x44,
        0,
        8,
    },
    {
        "curr2_max",
        NULL,
        chip_limit_show,
        NULL,
        0x46,
        0,
        8,
    },
};

static i2c_dev_data_st chip_data;

/* chip_driver id */
static const struct i2c_device_id chip_id[] = {
    {"pxe1410", 0},
    {},
};
MODULE_DEVICE_TABLE(i2c, chip_id);

static int32_t chip_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int32_t n_attrs = sizeof(chip_attr_table) / sizeof(chip_attr_table[0]);

    return i2c_dev_sysfs_data_init(client, &chip_data, chip_attr_table, n_attrs);
}

static int32_t chip_remove(struct i2c_client *client)
{
    i2c_dev_sysfs_data_clean(client, &chip_data);
    return 0;
}

static struct i2c_driver chip_driver = {
    .class = I2C_CLASS_HWMON,
    .driver = {
        .name = "pxe1410",
    },
    .probe = chip_probe,
    .remove = chip_remove,
    .id_table = chip_id,
};

static int32_t __init chip_mod_init(void)
{
    return i2c_add_driver(&chip_driver);
}

static void __exit chip_mod_exit(void)
{
    i2c_del_driver(&chip_driver);
}

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("PXE1410 Driver");
MODULE_LICENSE("GPL");

module_init(chip_mod_init);
module_exit(chip_mod_exit);

reset_driver.sh

#!/bin/sh

sudo rmmod switchboard
sudo rmmod pxe1410
sudo rmmod optoe
sudo rmmod mc24lc64t
sudo rmmod ina2xx
sudo rmmod i2c-ocores
sudo rmmod i2c_dev_sysfs
sudo rmmod fpga_i2c_adapter
sudo rmmod cpld_lpc
sudo rmmod cpld_i2c

sleep 1

sudo modprobe i2c-i801
sudo modprobe i2c-dev

sleep 1

sudo insmod cpld_i2c.ko
sudo insmod cpld_lpc.ko
sudo insmod fpga_i2c_adapter.ko
sudo insmod i2c_dev_sysfs.ko
sudo insmod i2c-ocores.ko
sudo insmod ina2xx.ko
sudo insmod mc24lc64t.ko
sudo insmod i2c_kobj_sysfs.ko
#sudo insmod optoe.ko
sudo insmod pxe1410.ko
sudo insmod switchboard.ko
#sudo insmod i2c_optoe.ko

setup.py

#! /usr/bin/python

import os
import sys


def new_device(driver, addr, bus, devdir):
    if not os.path.exists(os.path.join(bus, devdir)):
        try:
            with open("%s/new_device" % bus, "w") as f:
                f.write("%s 0x%x\n" % (driver, addr))
        except Exception, e:
            print "Unexpected error initialize device %s:0x%x:%s: %s" % (driver, addr, bus, e)
    else:
        print("Device %s:%x:%s already exists." % (driver, addr, bus))


def new_i2c_device(driver, addr, bus_number):
    bus = '/sys/bus/i2c/devices/i2c-%d' % bus_number
    devdir = "%d-%4.4x" % (bus_number, addr)
    return new_device(driver, addr, bus, devdir)


def baseconfig():
    print("Initialize EEPROM driver")
    # tlv eeprom device
    new_i2c_device('24lc64t', 0x56, 0)   #设备,匹配mc24lc64t.c中24lc64t
    # SN1701022
    new_i2c_device('sn1701022', 0x66, 4)
    new_i2c_device('sn1701022', 0x64, 4)
    # lm75
    new_i2c_device('lm75', 0x48, 3)
    new_i2c_device('lm75', 0x49, 3)
    new_i2c_device('lm75', 0x48, 5)
    new_i2c_device('lm75', 0x49, 6)
    new_i2c_device('lm75', 0x4a, 7)
    new_i2c_device('lm75', 0x4b, 8)
    # xdpe132G
    new_i2c_device('xdpe132g5c', 0x4d, 9)
    # pxe1410 device
    new_i2c_device('pxe1410', 0x60, 10)
    # ADS7830 device
    new_i2c_device('ads7830', 0x48, 11)
    new_i2c_device('ads7830', 0x49, 12)
    new_i2c_device('ads7830', 0x4a, 13)
    # TPA620 device
    new_i2c_device('tpa620', 0x41, 30)
    for y in range(16):
        new_i2c_device('optoe1', 0x50, 14 + y)


def main():
    baseconfig()


if __name__ == "__main__":
    sys.exit(main())

sn1701022.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2020 Facebook Inc.
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include "pmbus.h"

#define MAX_SUPPORT_PAGE 2
static struct pmbus_driver_info sn1701022_info = {
    .pages = MAX_SUPPORT_PAGE,
    .format[PSC_VOLTAGE_IN] = linear,
    .format[PSC_VOLTAGE_OUT] = vid,
    .vrm_version[0] = vr12,
    .vrm_version[1] = vr12,
    .format[PSC_CURRENT_IN] = linear,
    .format[PSC_CURRENT_OUT] = linear,
    .format[PSC_TEMPERATURE] = linear,
    .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT,
    .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT,
};

static int32_t sn1701022_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    return pmbus_do_probe(client, id, &sn1701022_info);
}

static const struct i2c_device_id sn1701022_id[] = {{"sn1701022", 0}, {}};

MODULE_DEVICE_TABLE(i2c, sn1701022_id);

static struct i2c_driver sn1701022_driver = {
    .driver = {
        .name = "sn1701022",
    },
    .probe = sn1701022_probe,
    .remove = pmbus_do_remove,
    .id_table = sn1701022_id,
};

module_i2c_driver(sn1701022_driver);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("PMBus driver for SN1701022");
MODULE_LICENSE("GPL");

fpga驱动,设备在fpga_i2c_adapter.c

你可能感兴趣的:(Notes,linux)