[device]: Add a new supported device, Delta-ag5648 (#1470)
* [platform]: Add a new supported platform, Delta-ag5648 CPU : Intel Rangeley C2538 Swich ASIC: Broadcom Tomahawk BCM56967 Ports : 48x25G + 6x100G Switch SKU : Delta-ag5648 Signed-off-by: neal tai <neal.tai@deltaww.com> * Delete the file Delete the auto-generated file. * [device]: ag5648 remove all *.cmd files. remove the files under ag5648/modules Signed-off-by: neal tai <neal.tai@deltaww.com> * [device]: ag5648 device drivers 1. Use the common driver dni_emc2305.c ag9032v1 device drivers 1. Move dni_emc2305.c to be the common driver 2. Remove at24.c Signed-off-by: neal tai <neal.tai@deltaww.com>
This commit is contained in:
parent
dba35eebb1
commit
830e1dd560
@ -0,0 +1,55 @@
|
||||
# name lanes alias
|
||||
Ethernet0 65 twentyfiveGigE1
|
||||
Ethernet4 66 twentyfiveGigE2
|
||||
Ethernet8 67 twentyfiveGigE3
|
||||
Ethernet12 68 twentyfiveGigE4
|
||||
Ethernet16 69 twentyfiveGigE5
|
||||
Ethernet20 70 twentyfiveGigE6
|
||||
Ethernet24 71 twentyfiveGigE7
|
||||
Ethernet28 72 twentyfiveGigE8
|
||||
Ethernet32 81 twentyfiveGigE9
|
||||
Ethernet36 82 twentyfiveGigE10
|
||||
Ethernet40 83 twentyfiveGigE11
|
||||
Ethernet44 84 twentyfiveGigE12
|
||||
Ethernet48 85 twentyfiveGigE13
|
||||
Ethernet52 86 twentyfiveGigE14
|
||||
Ethernet56 87 twentyfiveGigE15
|
||||
Ethernet60 88 twentyfiveGigE16
|
||||
Ethernet64 33 twentyfiveGigE17
|
||||
Ethernet68 34 twentyfiveGigE18
|
||||
Ethernet72 35 twentyfiveGigE19
|
||||
Ethernet76 36 twentyfiveGigE20
|
||||
Ethernet80 37 twentyfiveGigE21
|
||||
Ethernet84 38 twentyfiveGigE22
|
||||
Ethernet88 39 twentyfiveGigE23
|
||||
Ethernet92 40 twentyfiveGigE24
|
||||
Ethernet96 42 twentyfiveGigE25
|
||||
Ethernet100 41 twentyfiveGigE26
|
||||
Ethernet104 44 twentyfiveGigE27
|
||||
Ethernet108 43 twentyfiveGigE28
|
||||
Ethernet112 49 twentyfiveGigE29
|
||||
Ethernet116 50 twentyfiveGigE30
|
||||
Ethernet120 51 twentyfiveGigE31
|
||||
Ethernet124 52 twentyfiveGigE32
|
||||
Ethernet128 53 twentyfiveGigE33
|
||||
Ethernet132 54 twentyfiveGigE34
|
||||
Ethernet136 55 twentyfiveGigE35
|
||||
Ethernet140 56 twentyfiveGigE36
|
||||
Ethernet144 97 twentyfiveGigE37
|
||||
Ethernet148 98 twentyfiveGigE38
|
||||
Ethernet152 99 twentyfiveGigE39
|
||||
Ethernet156 100 twentyfiveGigE40
|
||||
Ethernet160 101 twentyfiveGigE41
|
||||
Ethernet164 102 twentyfiveGigE42
|
||||
Ethernet168 103 twentyfiveGigE43
|
||||
Ethernet172 104 twentyfiveGigE44
|
||||
Ethernet176 105 twentyfiveGigE45
|
||||
Ethernet180 106 twentyfiveGigE46
|
||||
Ethernet184 107 twentyfiveGigE47
|
||||
Ethernet188 108 twentyfiveGigE48
|
||||
Ethernet192 117,118,119,120 hundredGigE51
|
||||
Ethernet196 109,110,111,112 hundredGigE51
|
||||
Ethernet200 5,6,7,8 hundredGigE51
|
||||
Ethernet204 1,2,3,4 hundredGigE52
|
||||
Ethernet208 21,22,23,24 hundredGigE53
|
||||
Ethernet212 9,10,11,12 hundredGigE54
|
@ -0,0 +1 @@
|
||||
SAI_INIT_CONFIG_FILE=/etc/bcm/th-ag5648-48x25G+6x100G.config.bcm
|
10
device/delta/x86_64-delta_ag5648-r0/fancontrol
Normal file
10
device/delta/x86_64-delta_ag5648-r0/fancontrol
Normal file
@ -0,0 +1,10 @@
|
||||
INTERVAL=10
|
||||
DEVPATH=hwmon1=/sys/bus/i2c/devices
|
||||
DEVNAME=hwmon1=emc2305
|
||||
FCTEMPS=DEVPATH/2-004d/hwmon/hwmon*/temp1_input DEVPATH/3-0049/hwmon/hwmon*/temp1_input DEVPATH/3-004b/hwmon/hwmon*/temp1_input DEVPATH/3-004c/hwmon/hwmon*/temp1_input DEVPATH/3-004e/hwmon/hwmon*/temp1_input DEVPATH/3-004f/hwmon/hwmon*/temp1_input DEVPATH/6-0059/temp1_input DEVPATH/6-0058/temp1_input
|
||||
|
||||
FCFANS=DEVPATH/3-004d/fan1_input DEVPATH/3-004d/fan2_input DEVPATH/3-004d/fan3_input DEVPATH/3-004d/fan4_input DEVPATH/5-004d/fan1_input DEVPATH/5-004d/fan2_input DEVPATH/5-004d/fan3_input DEVPATH/5-004d/fan4_input
|
||||
MINTEMP=20
|
||||
MAXTEMP=60
|
||||
MINSTART=75
|
||||
MINSTOP=22
|
262
device/delta/x86_64-delta_ag5648-r0/fancontrol.service
Executable file
262
device/delta/x86_64-delta_ag5648-r0/fancontrol.service
Executable file
@ -0,0 +1,262 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Simple script implementing a temperature dependent fan speed control
|
||||
# Supported Linux kernel versions: 2.6.5 and later
|
||||
#
|
||||
# Version 0.70
|
||||
#
|
||||
# Usage: fancontrol.service [CONFIGFILE]
|
||||
#
|
||||
# Dependencies:
|
||||
# bash, egrep, sed, cut, sleep, readlink, lm_sensors :)
|
||||
#
|
||||
# Please send any questions, comments or success stories to
|
||||
# marius.reiner@hdev.de
|
||||
# Thanks!
|
||||
#
|
||||
# For configuration instructions and warnings please see fancontrol.txt, which
|
||||
# can be found in the doc/ directory or at the website mentioned above.
|
||||
#
|
||||
#
|
||||
# Copyright 2003 Marius Reiner <marius.reiner@hdev.de>
|
||||
# Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
|
||||
#
|
||||
# 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301 USA.
|
||||
#
|
||||
#
|
||||
|
||||
PIDFILE="/var/run/fancontrol.pid"
|
||||
|
||||
#DEBUG=1
|
||||
MAX=255
|
||||
|
||||
function LoadConfig
|
||||
{
|
||||
local fcvcount fcv
|
||||
|
||||
echo "Loading configuration from $1 ..."
|
||||
if [ ! -r "$1" ]
|
||||
then
|
||||
echo "Error: Can't read configuration file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# grep configuration from file
|
||||
INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL=//g'`
|
||||
DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'`
|
||||
DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'`
|
||||
FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS=//g'`
|
||||
MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP=//g'`
|
||||
MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP=//g'`
|
||||
MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART=//g'`
|
||||
MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP=//g'`
|
||||
HWMON=$( echo "$DEVPATH" | sed 's/=.*$//g')
|
||||
FCDEVPATH=$( echo "$DEVPATH" | sed 's/^.*=//g')
|
||||
FCMINTEMP=$MINTEMP
|
||||
FCMAXTEMP=$MAXTEMP
|
||||
FCMINSTART=$MINSTART
|
||||
FCMINSTOP=$MINSTOP
|
||||
|
||||
FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS=//g'`
|
||||
|
||||
# Check whether all mandatory settings are set
|
||||
if [[ -z ${INTERVAL} || -z ${FCTEMPS} || -z ${MINTEMP} || -z ${MAXTEMP} || -z ${MINSTART} || -z ${MINSTOP} ]]
|
||||
then
|
||||
echo "Some mandatory settings missing, please check your config file!" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$INTERVAL" -le 0 ]
|
||||
then
|
||||
echo "Error in configuration file:" >&2
|
||||
echo "INTERVAL must be at least 1" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# write settings to arrays for easier use and print them
|
||||
echo "Common settings:"
|
||||
|
||||
temp_string=$FCTEMPS
|
||||
|
||||
let fcvcount=0
|
||||
for fcv in $FCTEMPS
|
||||
do
|
||||
fcvcount=$((fcvcount+1))
|
||||
AFCTEMP[$fcvcount]=$( echo "$temp_string" | cut -d" " -f $fcvcount )
|
||||
AFCTEMP[$fcvcount]=$( echo "${AFCTEMP[$fcvcount]}" | sed 's/DEVPATH/\/sys\/bus\/i2c\/devices/g' )
|
||||
AFCTEMP_PATH[$fcvcount]=$( echo "${AFCTEMP[$fcvcount]}" | sed 's/DEVPATH/\/sys\/bus\/i2c\/devices/g' )
|
||||
|
||||
#if [ $fcvcount -eq 5 ];then
|
||||
#echo "0x00" > '/sys/bus/i2c/devices/6-0058/psu_select_member'
|
||||
# AFCTEMP[$fcvcount]=$( cat ${AFCTEMP[$fcvcount]} )
|
||||
#elif [ $fcvcount -eq 6 ];then
|
||||
#echo "0x20" > '/sys/bus/i2c/devices/6-0058/psu_select_member'
|
||||
# AFCTEMP[$fcvcount]=$( cat ${AFCTEMP[$fcvcount]} )
|
||||
#else
|
||||
# AFCTEMP[$fcvcount]=$( cat ${AFCTEMP[$fcvcount]} )
|
||||
#fi
|
||||
AFCTEMP[$fcvcount]=$( cat ${AFCTEMP[$fcvcount]} )
|
||||
AFCTEMP[$fcvcount]=$(( AFCTEMP[$fcvcount]/1000 ))
|
||||
echo "AFCTEMP[$fcvcount]=${AFCTEMP[$fcvcount]} (Celsius)"
|
||||
done
|
||||
|
||||
fan_string=$FCFANS
|
||||
fcvcount=0
|
||||
zero=0
|
||||
for fcv in $FCFANS
|
||||
do
|
||||
fcvcount=$((fcvcount+1))
|
||||
AFCFAN[$fcvcount]=$( echo "$fan_string" | cut -d" " -f $fcvcount )
|
||||
AFCFAN_PATH[$fcvcount]=$( echo "${AFCFAN[$fcvcount]}" | sed 's/DEVPATH/\/sys\/bus\/i2c\/devices/g' )
|
||||
AFCFAN_TARGET[$fcvcount]=$( echo "${AFCFAN_PATH[$fcvcount]}" | sed 's/DEVPATH/\/sys\/bus\/i2c\/devices/g' )
|
||||
AFCFAN_TARGET[$fcvcount]=$( echo "${AFCFAN_TARGET[$fcvcount]}" | sed 's/$/_percentage/g')
|
||||
AFCFAN[$fcvcount]=$( cat ${AFCFAN_PATH[$fcvcount]} )
|
||||
if [ "${AFCFAN[$fcvcount]}" == 960 ]
|
||||
then
|
||||
AFCFAN[$fcvcount]=$zero
|
||||
fi
|
||||
echo "AFCFAN[$fcvcount]=${AFCFAN[$fcvcount]} (rpm)"
|
||||
done
|
||||
echo "INTERVAL=$INTERVAL"
|
||||
echo "DEVPATH=$FCDEVPATH"
|
||||
echo "MINTEMP=$FCMINTEMP"
|
||||
echo "MAXTEMP=$FCMAXTEMP"
|
||||
echo "MINSTART=$FCMINSTART"
|
||||
echo "MINSTOP=$FCMINSTOP"
|
||||
}
|
||||
|
||||
# Check that all referenced sysfs files exist
|
||||
function CheckFiles
|
||||
{
|
||||
local outdated=0 fcvcount tsen fan
|
||||
if [ $outdated -eq 1 ]
|
||||
then
|
||||
echo >&2
|
||||
echo "At least one referenced file is missing. Either some required kernel" >&2
|
||||
echo "modules haven't been loaded, or your configuration file is outdated." >&2
|
||||
echo "In the latter case, you should run pwmconfig again." >&2
|
||||
fi
|
||||
return $outdated
|
||||
}
|
||||
|
||||
LoadConfig $1
|
||||
|
||||
# Detect path to sensors
|
||||
if [ ! -d $DIR ]
|
||||
then
|
||||
echo $0: 'No sensors found! (did you load the necessary modules?)' >&2
|
||||
exit 1
|
||||
fi
|
||||
cd $DIR
|
||||
|
||||
# Check for configuration change
|
||||
if [ "$DIR" != "/" ] && [ -z "$DEVPATH" -o -z "$DEVNAME" ]
|
||||
then
|
||||
echo "Configuration is too old, please run pwmconfig again" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$DIR" = "/" -a -n "$DEVPATH" ]
|
||||
then
|
||||
echo "Unneeded DEVPATH with absolute device paths" >&2
|
||||
exit 1
|
||||
fi
|
||||
CheckFiles || exit 1
|
||||
|
||||
if [ -f "$PIDFILE" ]
|
||||
then
|
||||
echo "File $PIDFILE exists, is fancontrol already running?" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo $$ > "$PIDFILE"
|
||||
|
||||
|
||||
# main function
|
||||
function UpdateThermalSensors
|
||||
{
|
||||
echo ""
|
||||
TEMP_HIGHEST=0
|
||||
FAN_PERCENTAGE=0
|
||||
|
||||
for i in ${AFCTEMP_PATH[@]}; do
|
||||
if (( $(cat $i) > $TEMP_HIGHEST )); then
|
||||
TEMP_HIGHEST=$(cat $i);
|
||||
fi;
|
||||
done
|
||||
TEMP_HIGHEST=$((TEMP_HIGHEST/1000))
|
||||
echo "The highest temperature of thermal sensors: $TEMP_HIGHEST C"
|
||||
|
||||
}
|
||||
|
||||
function UpdateFanSpeeds
|
||||
{
|
||||
if [ $TEMP_HIGHEST -lt 51 ]; then #TEMP<=50
|
||||
FAN_PERCENTAGE=40
|
||||
elif [ $TEMP_HIGHEST -lt 56 -a $TEMP_HIGHEST -gt 50 ]; then #50<TEMP<=55
|
||||
FAN_PERCENTAGE=60
|
||||
elif [ $TEMP_HIGHEST -lt 61 -a $TEMP_HIGHEST -gt 55 ]; then #55<TEMP<=60
|
||||
FAN_PERCENTAGE=80
|
||||
elif [ $TEMP_HIGHEST -lt 66 -a $TEMP_HIGHEST -gt 60 ]; then #60<TEMP<=65
|
||||
FAN_PERCENTAGE=90
|
||||
elif [ $TEMP_HIGHEST -gt 65 ]; then # 65<TEMP
|
||||
FAN_PERCENTAGE=100
|
||||
else
|
||||
FAN_PERCENTAGE=100
|
||||
fi
|
||||
|
||||
echo "Trying to set fan speed to $FAN_PERCENTAGE %"
|
||||
#Set speed to fan1~fan10
|
||||
let fcvcount=0
|
||||
for fcv in $FCFANS
|
||||
do
|
||||
fcvcount=$(( fcvcount + 1 ))
|
||||
echo $FAN_PERCENTAGE > ${AFCFAN_TARGET[$fcvcount]}
|
||||
AFCFAN[$fcvcount]=$( cat ${AFCFAN_PATH[$fcvcount]} )
|
||||
|
||||
if [ "${AFCFAN[$fcvcount]}" == 960 ]
|
||||
then
|
||||
AFCFAN[$fcvcount]=$zero
|
||||
fi
|
||||
echo "AFCFAN[$fcvcount]=${AFCFAN[$fcvcount]} (rpm)"
|
||||
done
|
||||
|
||||
if [ $TEMP_HIGHEST -lt 51 ]; then #TEMP<=50
|
||||
FAN_ON_PSU_PERCENTAGE=50
|
||||
elif [ $TEMP_HIGHEST -lt 100 -a $TEMP_HIGHEST -gt 50 ]; then #50<TEMP<100
|
||||
FAN_ON_PSU_PERCENTAGE=100
|
||||
else
|
||||
echo "Unable to get thermal temperature"
|
||||
FAN_ON_PSU_PERCENTAGE=100
|
||||
fi
|
||||
#Set speed to PSU_FAN1
|
||||
#echo "0x00" > '/sys/bus/i2c/devices/4-0058/psu_select_member'
|
||||
echo "$FAN_ON_PSU_PERCENTAGE" > '/sys/bus/i2c/devices/6-0059/fan1_set_percentage'
|
||||
echo "PSU fan1 =$( cat '/sys/bus/i2c/devices/6-0059/fan1_input' ) (rpm)"
|
||||
#Set speed to PSU_FAN2
|
||||
#echo "0x20" > '/sys/bus/i2c/devices/6-0058/psu_select_member'
|
||||
echo "$FAN_ON_PSU_PERCENTAGE" > '/sys/bus/i2c/devices/6-0058/fan1_set_percentage'
|
||||
echo "PSU fan2 =$( cat '/sys/bus/i2c/devices/6-0058/fan1_input' ) (rpm)"
|
||||
|
||||
rm -f "$PIDFILE"
|
||||
}
|
||||
# main loop calling the main function at specified intervals
|
||||
while true
|
||||
do
|
||||
UpdateThermalSensors
|
||||
UpdateFanSpeeds
|
||||
echo "Sleep $INTERVAL seconds ..."
|
||||
# Sleep while still handling signals
|
||||
sleep $INTERVAL &
|
||||
wait $!
|
||||
done
|
2
device/delta/x86_64-delta_ag5648-r0/installer.conf
Normal file
2
device/delta/x86_64-delta_ag5648-r0/installer.conf
Normal file
@ -0,0 +1,2 @@
|
||||
CONSOLE_PORT=0x3f8
|
||||
CONSOLE_SPEED=115200
|
67
device/delta/x86_64-delta_ag5648-r0/led_proc_init.soc
Executable file
67
device/delta/x86_64-delta_ag5648-r0/led_proc_init.soc
Executable file
@ -0,0 +1,67 @@
|
||||
#AG5648 LED
|
||||
|
||||
led 0 stop
|
||||
led 0 prog \
|
||||
02 00 60 E0 02 A0 60 E2 86 ED 02 00 60 E1 67 EF \
|
||||
71 1F 2E E0 32 08 97 02 00 0E 02 60 E3 77 2A 2E \
|
||||
E0 32 08 97 02 00 0E 03 60 E3 2E E0 32 00 32 01 \
|
||||
B7 97 02 00 0E 00 12 E7 FE E1 50 67 EF 71 49 86 \
|
||||
E0 86 E1 06 E1 D2 04 74 2A 02 00 60 E1 12 E7 FE \
|
||||
E1 05 60 EC 12 E3 FE E1 05 0A 03 71 61 67 EB 77 \
|
||||
63 67 CB 67 EF 71 76 12 E3 FE E1 05 0A 02 71 74 \
|
||||
67 EB 77 76 67 CB 12 E3 FE E1 05 0A 01 71 87 67 \
|
||||
EB 67 EF 71 97 77 8D 67 CB 67 EF 71 97 86 E1 06 \
|
||||
E1 D2 04 74 4D 77 A5 06 E0 28 32 08 97 71 A3 67 \
|
||||
EB 77 A5 67 D7 06 E2 67 EF 71 B1 F2 04 60 E2 77 \
|
||||
B7 F2 01 60 E2 86 E0 06 E0 D2 24 74 0A 67 EB 67 \
|
||||
EB 67 EB 67 EB 67 EB 67 EB 3A 72 67 EF 71 E7 06 \
|
||||
EC D2 00 70 E7 77 DD 06 EC D2 00 70 EB 16 ED 99 \
|
||||
99 1A 00 71 E7 77 EB 32 0F 87 57 32 0E 87 57 16 \
|
||||
E0 DA 0C 57 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
led 0 auto on
|
||||
led 0 start
|
||||
|
||||
led 1 stop
|
||||
led 1 prog \
|
||||
02 00 60 E0 02 A0 60 E2 86 E6 02 00 60 E1 2E E0 \
|
||||
32 08 97 02 00 0E 03 60 E3 2E E0 32 00 32 01 B7 \
|
||||
97 02 00 0E 00 12 E4 FE E1 50 12 E4 05 60 E5 12 \
|
||||
E3 05 0A 03 71 3A 67 83 77 3C 67 7F 12 E3 05 0A \
|
||||
01 71 47 67 83 77 49 67 7F 06 E0 28 32 08 97 71 \
|
||||
55 67 83 77 57 67 6F 06 E2 F2 01 60 E2 86 E0 06 \
|
||||
E0 D2 24 74 0A 67 83 67 83 67 83 67 83 3A 70 06 \
|
||||
E5 D2 00 70 83 16 E6 99 99 1A 00 71 7F 77 83 32 \
|
||||
0F 87 57 32 0E 87 57 00 00 00 00 00 00 00 00 00
|
||||
led 1 auto on
|
||||
led 1 start
|
||||
|
||||
# LED0 port order remap
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_0_3 REMAP_PORT_0=47 REMAP_PORT_1=47 REMAP_PORT_2=47 REMAP_PORT_3=47
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_4_7 REMAP_PORT_4=47 REMAP_PORT_5=47 REMAP_PORT_6=47 REMAP_PORT_7=47
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_8_11 REMAP_PORT_8=31 REMAP_PORT_9=30 REMAP_PORT_10=29 REMAP_PORT_11=28
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_12_15 REMAP_PORT_12=47 REMAP_PORT_13=47 REMAP_PORT_14=47 REMAP_PORT_15=47
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_16_19 REMAP_PORT_16=47 REMAP_PORT_17=47 REMAP_PORT_18=47 REMAP_PORT_19=47
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_20_23 REMAP_PORT_20=35 REMAP_PORT_21=34 REMAP_PORT_22=33 REMAP_PORT_23=32
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_24_27 REMAP_PORT_24=23 REMAP_PORT_25=22 REMAP_PORT_26=21 REMAP_PORT_27=20
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_28_31 REMAP_PORT_28=27 REMAP_PORT_29=26 REMAP_PORT_30=25 REMAP_PORT_31=24
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_32_35 REMAP_PORT_32=47 REMAP_PORT_33=47 REMAP_PORT_34=47 REMAP_PORT_35=47
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_40_43 REMAP_PORT_40=15 REMAP_PORT_41=14 REMAP_PORT_42=13 REMAP_PORT_43=12
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_48_51 REMAP_PORT_48=19 REMAP_PORT_49=18 REMAP_PORT_50=17 REMAP_PORT_51=16
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_52_55 REMAP_PORT_52=11 REMAP_PORT_53=10 REMAP_PORT_54=9 REMAP_PORT_55=8
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_56_59 REMAP_PORT_56=7 REMAP_PORT_57=6 REMAP_PORT_58=5 REMAP_PORT_59=4
|
||||
m CMIC_LEDUP0_PORT_ORDER_REMAP_60_63 REMAP_PORT_60=3 REMAP_PORT_61=2 REMAP_PORT_62=1 REMAP_PORT_63=0
|
||||
|
||||
# LED1 port order remap
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_0_3 REMAP_PORT_0=19 REMAP_PORT_1=18 REMAP_PORT_2=17 REMAP_PORT_3=16
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_4_7 REMAP_PORT_4=23 REMAP_PORT_5=22 REMAP_PORT_6=21 REMAP_PORT_7=20
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_8_11 REMAP_PORT_8=26 REMAP_PORT_9=27 REMAP_PORT_10=24 REMAP_PORT_11=25
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_12_15 REMAP_PORT_12=63 REMAP_PORT_13=63 REMAP_PORT_14=63 REMAP_PORT_15=63
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_16_19 REMAP_PORT_16=31 REMAP_PORT_17=30 REMAP_PORT_18=29 REMAP_PORT_19=28
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_20_23 REMAP_PORT_20=35 REMAP_PORT_21=34 REMAP_PORT_22=33 REMAP_PORT_23=32
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_24_27 REMAP_PORT_24=63 REMAP_PORT_25=63 REMAP_PORT_26=63 REMAP_PORT_27=63
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_28_31 REMAP_PORT_28=63 REMAP_PORT_29=63 REMAP_PORT_30=63 REMAP_PORT_31=63
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_32_35 REMAP_PORT_32=3 REMAP_PORT_33=2 REMAP_PORT_34=1 REMAP_PORT_35=0
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_36_39 REMAP_PORT_36=7 REMAP_PORT_37=6 REMAP_PORT_38=5 REMAP_PORT_39=4
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_48_51 REMAP_PORT_48=11 REMAP_PORT_49=10 REMAP_PORT_50=9 REMAP_PORT_51=8
|
||||
m CMIC_LEDUP1_PORT_ORDER_REMAP_52_55 REMAP_PORT_52=15 REMAP_PORT_53=14 REMAP_PORT_54=13 REMAP_PORT_55=12
|
||||
|
1184
device/delta/x86_64-delta_ag5648-r0/minigraph.xml
Normal file
1184
device/delta/x86_64-delta_ag5648-r0/minigraph.xml
Normal file
File diff suppressed because it is too large
Load Diff
32
device/delta/x86_64-delta_ag5648-r0/plugins/eeprom.py
Normal file
32
device/delta/x86_64-delta_ag5648-r0/plugins/eeprom.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#############################################################################
|
||||
# Mellanox
|
||||
#
|
||||
# Platform and model specific eeprom subclass, inherits from the base class,
|
||||
# and provides the followings:
|
||||
# - the eeprom format definition
|
||||
# - specific encoder/decoder if there is special need
|
||||
#############################################################################
|
||||
|
||||
try:
|
||||
import exceptions
|
||||
import binascii
|
||||
import time
|
||||
import optparse
|
||||
import warnings
|
||||
import os
|
||||
import sys
|
||||
from sonic_eeprom import eeprom_base
|
||||
from sonic_eeprom import eeprom_tlvinfo
|
||||
import subprocess
|
||||
except ImportError, e:
|
||||
raise ImportError (str(e) + "- required module not found")
|
||||
|
||||
class board(eeprom_tlvinfo.TlvInfoDecoder):
|
||||
|
||||
_TLV_INFO_MAX_LEN = 256
|
||||
|
||||
def __init__(self, name, path, cpld_root, ro):
|
||||
self.eeprom_path = "/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-2/2-0053/eeprom"
|
||||
super(board, self).__init__(self.eeprom_path, 0, '', True)
|
56
device/delta/x86_64-delta_ag5648-r0/plugins/psuutil.py
Normal file
56
device/delta/x86_64-delta_ag5648-r0/plugins/psuutil.py
Normal file
@ -0,0 +1,56 @@
|
||||
import os.path
|
||||
|
||||
try:
|
||||
from sonic_psu.psu_base import PsuBase
|
||||
except ImportError as e:
|
||||
raise ImportError (str(e) + "- required module not found")
|
||||
|
||||
class PsuUtil(PsuBase):
|
||||
"""Platform-specific PSUutil class"""
|
||||
|
||||
def __init__(self):
|
||||
PsuBase.__init__(self)
|
||||
|
||||
self.psu_path = "/sys/bus/i2c/devices/6-00{}/"
|
||||
self.psu_oper_status = "in1_input"
|
||||
self.psu_presence = "i2cget -y 6 0x{} 0x00"
|
||||
|
||||
|
||||
def get_num_psus(self):
|
||||
"""
|
||||
Retrieves the number of PSUs available on the device
|
||||
|
||||
:return: An integer, the number of PSUs available on the device
|
||||
"""
|
||||
return 2
|
||||
|
||||
def get_psu_status(self, index):
|
||||
if index is None:
|
||||
return False
|
||||
Base_bus_number = 57
|
||||
status = 0
|
||||
try:
|
||||
with open(self.psu_path.format(index + Base_bus_number) + self.psu_oper_status, 'r') as power_status:
|
||||
if int(power_status.read()) == 0 :
|
||||
return False
|
||||
else:
|
||||
status = 1
|
||||
except IOError:
|
||||
return False
|
||||
return status == 1
|
||||
|
||||
def get_psu_presence(self, index):
|
||||
if index is None:
|
||||
return False
|
||||
Base_bus_number = 49
|
||||
status = 0
|
||||
try:
|
||||
p = os.popen(self.psu_presence.format(index + Base_bus_number)+ "> /dev/null 2>&1")
|
||||
if p.readline() != None:
|
||||
status = 1
|
||||
p.close()
|
||||
except IOError:
|
||||
return False
|
||||
return status == 1
|
||||
|
||||
|
203
device/delta/x86_64-delta_ag5648-r0/plugins/sfputil.py
Normal file
203
device/delta/x86_64-delta_ag5648-r0/plugins/sfputil.py
Normal file
@ -0,0 +1,203 @@
|
||||
# sfputil.py
|
||||
#
|
||||
# Platform-specific SFP transceiver interface for SONiC
|
||||
#
|
||||
|
||||
try:
|
||||
import time
|
||||
from sonic_sfp.sfputilbase import SfpUtilBase
|
||||
except ImportError as e:
|
||||
raise ImportError("%s - required module not found" % str(e))
|
||||
|
||||
|
||||
class SfpUtil(SfpUtilBase):
|
||||
"""Platform-specific SfpUtil class"""
|
||||
|
||||
PORT_START = 0
|
||||
PORT_START_QSFP = 48
|
||||
PORT_END = 53
|
||||
PORTS_IN_BLOCK = 54
|
||||
|
||||
EEPROM_OFFSET = 10
|
||||
|
||||
_port_to_eeprom_mapping = {}
|
||||
|
||||
@property
|
||||
def port_start(self):
|
||||
return self.PORT_START
|
||||
@property
|
||||
def port_start_qsfp(self):
|
||||
return self.PORT_START_QSFP
|
||||
|
||||
@property
|
||||
def port_end(self):
|
||||
return self.PORT_END
|
||||
|
||||
@property
|
||||
def qsfp_ports(self):
|
||||
return range(0, self.PORTS_IN_BLOCK + 1)
|
||||
|
||||
@property
|
||||
def port_to_eeprom_mapping(self):
|
||||
return self._port_to_eeprom_mapping
|
||||
|
||||
def __init__(self):
|
||||
eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
|
||||
|
||||
for x in range(0, self.port_end + 1):
|
||||
if x > self.port_start_qsfp -1 and x < self.port_end + 1:
|
||||
self.get_response(x)
|
||||
self._port_to_eeprom_mapping[x] = eeprom_path.format(x + self.EEPROM_OFFSET)
|
||||
SfpUtilBase.__init__(self)
|
||||
|
||||
def get_response(self, port_num):
|
||||
# Check for invalid port_num
|
||||
if port_num < self.port_start_qsfp or port_num > self.port_end:
|
||||
return False
|
||||
|
||||
try:
|
||||
reg_file = open("/sys/devices/platform/delta-ag5648-cpld.0/sfp_response", "r+")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return False
|
||||
|
||||
# set the bit corresponding to our port
|
||||
mask = (port_num + 1) % 8 - 1
|
||||
mask = 1 << mask
|
||||
|
||||
# Convert our register value back to a hex string and write back
|
||||
content = hex(mask)
|
||||
reg_file.seek(0)
|
||||
reg_file.write(content)
|
||||
reg_file.close()
|
||||
|
||||
return True
|
||||
|
||||
def get_presence(self, port_num):
|
||||
# Check for invalid port_num
|
||||
if port_num < self.port_start or port_num > self.port_end:
|
||||
return False
|
||||
|
||||
try:
|
||||
reg_file = open("/sys/devices/platform/delta-ag5648-cpld.0/sfp_present")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return False
|
||||
|
||||
content = reg_file.readline().rstrip()
|
||||
|
||||
# content is a string containing the hex representation of the register
|
||||
reg_value = int(content, 16)
|
||||
|
||||
# Mask off the bit corresponding to our port
|
||||
mask = (1 << port_num)
|
||||
|
||||
# ModPrsL is active low
|
||||
if reg_value & mask == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_low_power_mode(self, port_num):
|
||||
# Check for invalid port_num
|
||||
if port_num < self.port_start or port_num > self.port_end:
|
||||
return False
|
||||
|
||||
try:
|
||||
reg_file = open("/sys/devices/platform/delta-ag5648-cpld.0/sfp_lpmode")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
|
||||
content = reg_file.readline().rstrip()
|
||||
|
||||
# content is a string containing the hex representation of the register
|
||||
reg_value = int(content, 16)
|
||||
|
||||
# Mask off the bit corresponding to our port
|
||||
mask = (1 << port_num)
|
||||
|
||||
# LPMode is active high
|
||||
if reg_value & mask == 0:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def set_low_power_mode(self, port_num, lpmode):
|
||||
# Check for invalid port_num
|
||||
if port_num < self.port_start_qsfp or port_num > self.port_end:
|
||||
return False
|
||||
|
||||
try:
|
||||
reg_file = open("/sys/devices/platform/delta-ag5648-cpld.0/sfp_lpmode", "r+")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return False
|
||||
|
||||
content = reg_file.readline().rstrip()
|
||||
|
||||
# content is a string containing the hex representation of the register
|
||||
reg_value = int(content, 16)
|
||||
|
||||
# Mask off the bit corresponding to our port
|
||||
mask = (1 << port_num)
|
||||
|
||||
# LPMode is active high; set or clear the bit accordingly
|
||||
if lpmode is True:
|
||||
reg_value = reg_value | mask
|
||||
else:
|
||||
reg_value = reg_value & ~mask
|
||||
|
||||
# Convert our register value back to a hex string and write back
|
||||
content = hex(reg_value)
|
||||
|
||||
reg_file.seek(0)
|
||||
reg_file.write(content)
|
||||
reg_file.close()
|
||||
|
||||
return True
|
||||
|
||||
def reset(self, port_num):
|
||||
QSFP_RESET_REGISTER_DEVICE_FILE = "/sys/devices/platform/delta-ag5648-cpld.0/sfp_reset"
|
||||
|
||||
# Check for invalid port_num
|
||||
if port_num < self.port_start_qsfp or port_num > self.port_end:
|
||||
return False
|
||||
|
||||
try:
|
||||
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "r+")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return False
|
||||
|
||||
content = reg_file.readline().rstrip()
|
||||
|
||||
# File content is a string containing the hex representation of the register
|
||||
reg_value = int(content, 16)
|
||||
|
||||
# Mask off the bit corresponding to our port
|
||||
mask = (1 << port_num)
|
||||
|
||||
# ResetL is active low
|
||||
reg_value = reg_value & ~mask
|
||||
|
||||
# Convert our register value back to a hex string and write back
|
||||
reg_file.seek(0)
|
||||
reg_file.write(hex(reg_value))
|
||||
reg_file.close()
|
||||
|
||||
# Sleep 1 second to allow it to settle
|
||||
time.sleep(1)
|
||||
|
||||
# Flip the bit back high and write back to the register to take port out of reset
|
||||
try:
|
||||
reg_file = open(QSFP_RESET_REGISTER_DEVICE_FILE, "w")
|
||||
except IOError as e:
|
||||
print "Error: unable to open file: %s" % str(e)
|
||||
return False
|
||||
|
||||
reg_value = reg_value | mask
|
||||
reg_file.seek(0)
|
||||
reg_file.write(hex(reg_value))
|
||||
reg_file.close()
|
||||
|
||||
return True
|
91
device/delta/x86_64-delta_ag5648-r0/sensors.conf
Normal file
91
device/delta/x86_64-delta_ag5648-r0/sensors.conf
Normal file
@ -0,0 +1,91 @@
|
||||
# libsensors configuration file for AG5648
|
||||
# ------------------------------------------------
|
||||
#
|
||||
|
||||
bus "i2c-2" "i2c-1-mux (chan_id 0)"
|
||||
bus "i2c-3" "i2c-1-mux (chan_id 1)"
|
||||
bus "i2c-4" "i2c-1-mux (chan_id 2)"
|
||||
bus "i2c-5" "i2c-1-mux (chan_id 3)"
|
||||
bus "i2c-6" "i2c-1-mux (chan_id 4)"
|
||||
bus "i2c-7" "i2c-1-mux (chan_id 5)"
|
||||
|
||||
# tmp75-i2c-2-4d board sensor near Left of front vents.
|
||||
# tmp75-i2c-3-49 board sensor near MAC.
|
||||
# tmp75-i2c-3-4b board sensor near Middle of front vents.
|
||||
# tmp75-i2c-3-4c board sensor near Right of front vents.
|
||||
# tmp75-i2c-3-4e board sensor near DC fan.
|
||||
# tmp75-i2c-3-4f board sensor near CPU.
|
||||
|
||||
chip "tmp75-i2c-2-4d"
|
||||
label temp1 "board sensor near Left of front vents"
|
||||
set temp1_max 60
|
||||
set temp1_max_hyst 55
|
||||
|
||||
chip "tmp75-i2c-3-49"
|
||||
label temp1 "board sensor near MAC"
|
||||
set temp1_max 85
|
||||
set temp1_max_hyst 80
|
||||
|
||||
chip "tmp75-i2c-3-4b"
|
||||
label temp1 "board sensor near Middle of front vents"
|
||||
set temp1_max 70
|
||||
set temp1_max_hyst 65
|
||||
|
||||
chip "tmp75-i2c-3-4c"
|
||||
label temp1 "board sensor near Right of front vents"
|
||||
set temp1_max 65
|
||||
set temp1_max_hyst 60
|
||||
|
||||
chip "tmp75-i2c-3-4e"
|
||||
label temp1 "board sensor near DC fan"
|
||||
set temp1_max 60
|
||||
set temp1_max_hyst 55
|
||||
|
||||
chip "tmp75-i2c-3-4f"
|
||||
label temp1 "board sensor near CPU"
|
||||
set temp1_max 80
|
||||
set temp1_max_hyst 75
|
||||
|
||||
chip "emc2305-i2c-3-4d"
|
||||
label fan1 "FANTRAY 1 REAR"
|
||||
label fan2 "FANTRAY 2 REAR"
|
||||
label fan3 "FANTRAY 3 REAR"
|
||||
label fan4 "FANTRAY 4 REAR"
|
||||
|
||||
chip "emc2305-i2c-5-4d"
|
||||
label fan1 "FANTRAY 1 FRONT"
|
||||
label fan2 "FANTRAY 2 FRONT"
|
||||
label fan3 "FANTRAY 3 FRONT"
|
||||
label fan4 "FANTRAY 4 FRONT"
|
||||
|
||||
chip "ltc4215-i2c-3-40"
|
||||
label in1 "PSU Hot-Swap voltage 1"
|
||||
label in2 "PSU Hot-Swap voltage 2"
|
||||
label power1 "PSU Hot-Swap power"
|
||||
label curr1 "PSU Hot-Swap current"
|
||||
|
||||
chip "ltc4215-i2c-3-42"
|
||||
label in1 "PSU Hot-Swap voltage 1"
|
||||
label in2 "PSU Hot-Swap voltage 2"
|
||||
label power1 "PSU Hot-Swap power"
|
||||
label curr1 "PSU Hot-Swap current"
|
||||
|
||||
chip "dni_ag5648_psu-i2c-6-59"
|
||||
label in1 "PSU voltage 1"
|
||||
label in2 "PSU voltage 2"
|
||||
label fan1 "PSU fan"
|
||||
label temp1 "PSU temperature"
|
||||
label power1 "PSU power1"
|
||||
label power2 "PSU power2"
|
||||
label curr1 "PSU current1"
|
||||
label curr2 "PSU current2"
|
||||
|
||||
chip "dni_ag5648_psu-i2c-6-58"
|
||||
label in1 "PSU voltage 1"
|
||||
label in2 "PSU voltage 2"
|
||||
label fan1 "PSU fan"
|
||||
label temp1 "PSU temperature"
|
||||
label power1 "PSU power1"
|
||||
label power2 "PSU power2"
|
||||
label curr1 "PSU current1"
|
||||
label curr2 "PSU current2"
|
@ -24,6 +24,7 @@ $(SONIC_ONE_IMAGE)_LAZY_INSTALLS += $(DELL_S6000_PLATFORM_MODULE) \
|
||||
$(CEL_DX010_PLATFORM_MODULE) \
|
||||
$(DELTA_AG9032V1_PLATFORM_MODULE) \
|
||||
$(DELTA_AG9064_PLATFORM_MODULE) \
|
||||
$(DELTA_AG5648_PLATFORM_MODULE) \
|
||||
$(QUANTA_IX1B_32X_PLATFORM_MODULE) \
|
||||
$(MITAC_LY1200_32X_PLATFORM_MODULE)
|
||||
$(SONIC_ONE_IMAGE)_DOCKERS += $(SONIC_INSTALL_DOCKER_IMAGES)
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
DELTA_AG9032V1_PLATFORM_MODULE_VERSION = 1.1
|
||||
DELTA_AG9064_PLATFORM_MODULE_VERSION = 1.1
|
||||
DELTA_AG5648_PLATFORM_MODULE_VERSION = 1.1
|
||||
|
||||
export DELTA_AG9032V1_PLATFORM_MODULE_VERSION
|
||||
export DELTA_AG9064_PLATFORM_MODULE_VERSION
|
||||
export DELTA_AG5648_PLATFORM_MODULE_VERSION
|
||||
|
||||
DELTA_AG9032V1_PLATFORM_MODULE = platform-modules-ag9032v1_$(DELTA_AG9032V1_PLATFORM_MODULE_VERSION)_amd64.deb
|
||||
$(DELTA_AG9032V1_PLATFORM_MODULE)_SRC_PATH = $(PLATFORM_PATH)/sonic-platform-modules-delta
|
||||
@ -16,3 +18,8 @@ SONIC_DPKG_DEBS += $(DELTA_AG9032V1_PLATFORM_MODULE)
|
||||
DELTA_AG9064_PLATFORM_MODULE = platform-modules-ag9064_$(DELTA_AG9064_PLATFORM_MODULE_VERSION)_amd64.deb
|
||||
$(DELTA_AG9064_PLATFORM_MODULE)_PLATFORM = x86_64-delta_ag9064-r0
|
||||
$(eval $(call add_extra_package,$(DELTA_AG9032V1_PLATFORM_MODULE),$(DELTA_AG9064_PLATFORM_MODULE)))
|
||||
|
||||
DELTA_AG5648_PLATFORM_MODULE = platform-modules-ag5648_$(DELTA_AG5648_PLATFORM_MODULE_VERSION)_amd64.deb
|
||||
$(DELTA_AG5648_PLATFORM_MODULE)_PLATFORM = x86_64-delta_ag5648-r0
|
||||
$(eval $(call add_extra_package,$(DELTA_AG9032V1_PLATFORM_MODULE),$(DELTA_AG5648_PLATFORM_MODULE)))
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
# /etc/modules: kernel modules to load at boot time.
|
||||
#
|
||||
# This file contains the names of kernel modules that should be loaded
|
||||
# at boot time, one per line. Lines beginning with "#" are ignored.
|
||||
|
||||
i2c-i801
|
||||
i2c-isch
|
||||
i2c-ismt
|
||||
i2c-dev
|
||||
i2c-mux
|
||||
i2c-smbus
|
||||
i2c-mux-gpio
|
||||
i2c-mux-pca954x
|
@ -0,0 +1,2 @@
|
||||
obj-m := dni_ag5648_psu.o dni_emc2305.o delta_ag5648_platform.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* An hwmon driver for delta AG5648 PSU
|
||||
* dps_800ab_16_d.c - Support for DPS-800AB-16 D Power Supply Module
|
||||
*
|
||||
* Copyright (C) 2016 Delta Network Technology Corporation
|
||||
*
|
||||
* DNI <DNIsales@delta.com.tw>
|
||||
*
|
||||
* Based on ym2651y.c
|
||||
* Based on ad7414.c
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#define MAX_FAN_DUTY_CYCLE 100
|
||||
#define SWPLD_REG 0x31
|
||||
#define SWPLD_PSU_MUX_REG 0x21
|
||||
#define SELECT_PSU1_EEPROM 0x00
|
||||
#define SELECT_PSU2_EEPROM 0x20
|
||||
|
||||
u8 psu_member_data = 0x00;
|
||||
|
||||
/* Address scanned */
|
||||
static const unsigned short normal_i2c[] = { 0x59, 0x58, I2C_CLIENT_END };
|
||||
|
||||
/* This is additional data */
|
||||
struct dps_800ab_16_d_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid;
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
/* Registers value */
|
||||
u8 vout_mode;
|
||||
u16 in1_input;
|
||||
u16 in2_input;
|
||||
u16 curr1_input;
|
||||
u16 curr2_input;
|
||||
u16 power1_input;
|
||||
u16 power2_input;
|
||||
u16 temp_input[2];
|
||||
u8 fan_target;
|
||||
u16 fan_duty_cycle_input[2];
|
||||
u16 fan_speed_input[2];
|
||||
u8 mfr_model[14];
|
||||
u8 mfr_serial[16];
|
||||
};
|
||||
|
||||
static int two_complement_to_int(u16 data, u8 valid_bit, int mask);
|
||||
static ssize_t set_fan_duty_cycle_input(struct device *dev, struct device_attribute \
|
||||
*dev_attr, const char *buf, size_t count);
|
||||
static ssize_t for_linear_data(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf);
|
||||
static ssize_t for_fan_target(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf);
|
||||
static ssize_t for_vout_data(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf);
|
||||
static int dps_800ab_16_d_read_byte(struct i2c_client *client, u8 reg);
|
||||
static int dps_800ab_16_d_read_word(struct i2c_client *client, u8 reg);
|
||||
static int dps_800ab_16_d_write_word(struct i2c_client *client, u8 reg, \
|
||||
u16 value);
|
||||
static int dps_800ab_16_d_read_block(struct i2c_client *client, u8 command, \
|
||||
u8 *data, int data_len);
|
||||
static struct dps_800ab_16_d_data *dps_800ab_16_d_update_device( \
|
||||
struct device *dev);
|
||||
static ssize_t for_ascii(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf);
|
||||
/*
|
||||
static ssize_t set_w_member_data(struct device *dev, struct device_attribute \
|
||||
*dev_att, const char *buf, size_t count);
|
||||
static ssize_t for_r_member_data(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf);
|
||||
*/
|
||||
enum dps_800ab_16_d_sysfs_attributes {
|
||||
PSU_V_IN,
|
||||
PSU_V_OUT,
|
||||
PSU_I_IN,
|
||||
PSU_I_OUT,
|
||||
PSU_P_IN,
|
||||
PSU_P_OUT,
|
||||
PSU_TEMP1_INPUT,
|
||||
PSU_FAN1_FAULT,
|
||||
PSU_FAN1_DUTY_CYCLE,
|
||||
PSU_FAN1_SPEED,
|
||||
PSU_MFR_MODEL,
|
||||
PSU_MFR_SERIAL,
|
||||
PSU_SELECT_MEMBER,
|
||||
};
|
||||
|
||||
static int two_complement_to_int(u16 data, u8 valid_bit, int mask)
|
||||
{
|
||||
u16 valid_data = data & mask;
|
||||
bool is_negative = valid_data >> (valid_bit - 1);
|
||||
|
||||
return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data;
|
||||
}
|
||||
|
||||
static ssize_t set_fan_duty_cycle_input(struct device *dev, struct device_attribute \
|
||||
*dev_attr, const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct dps_800ab_16_d_data *data = i2c_get_clientdata(client);
|
||||
int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1;
|
||||
long speed;
|
||||
int error;
|
||||
|
||||
error = kstrtol(buf, 10, &speed);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Select SWPLD PSU offset */
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan_duty_cycle_input[nr] = speed;
|
||||
dps_800ab_16_d_write_word(client, 0x3B + nr, data->fan_duty_cycle_input[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t for_linear_data(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
|
||||
struct dps_800ab_16_d_data *data = dps_800ab_16_d_update_device(dev);
|
||||
|
||||
u16 value = 0;
|
||||
int exponent, mantissa;
|
||||
int multiplier = 1000;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_V_IN:
|
||||
value = data->in1_input;
|
||||
break;
|
||||
case PSU_I_IN:
|
||||
value = data->curr1_input;
|
||||
break;
|
||||
case PSU_I_OUT:
|
||||
value = data->curr2_input;
|
||||
break;
|
||||
case PSU_P_IN:
|
||||
value = data->power1_input;
|
||||
multiplier = 1000*1000;
|
||||
break;
|
||||
case PSU_P_OUT:
|
||||
value = data->power2_input;
|
||||
multiplier = 1000*1000;
|
||||
break;
|
||||
case PSU_TEMP1_INPUT:
|
||||
value = data->temp_input[0];
|
||||
break;
|
||||
case PSU_FAN1_DUTY_CYCLE:
|
||||
multiplier = 1;
|
||||
value = data->fan_duty_cycle_input[0];
|
||||
break;
|
||||
case PSU_FAN1_SPEED:
|
||||
multiplier = 1;
|
||||
value = data->fan_speed_input[0];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exponent = two_complement_to_int(value >> 11, 5, 0x1f);
|
||||
mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff);
|
||||
|
||||
return (exponent >= 0) ? sprintf(buf, "%d\n", \
|
||||
(mantissa << exponent) * multiplier) : \
|
||||
sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent));
|
||||
}
|
||||
|
||||
static ssize_t for_fan_target(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
|
||||
struct dps_800ab_16_d_data *data = dps_800ab_16_d_update_device(dev);
|
||||
|
||||
u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6;
|
||||
|
||||
return sprintf(buf, "%d\n", data->fan_target >> shift);
|
||||
}
|
||||
|
||||
static ssize_t for_vout_data(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf)
|
||||
{
|
||||
struct dps_800ab_16_d_data *data = dps_800ab_16_d_update_device(dev);
|
||||
int exponent, mantissa;
|
||||
int multiplier = 1000;
|
||||
|
||||
exponent = two_complement_to_int(data->vout_mode, 5, 0x1f);
|
||||
mantissa = data->in2_input;
|
||||
|
||||
return (exponent > 0) ? sprintf(buf, "%d\n", \
|
||||
(mantissa * multiplier) / (1 << exponent)): \
|
||||
sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent));
|
||||
|
||||
}
|
||||
|
||||
static ssize_t for_ascii(struct device *dev, struct device_attribute \
|
||||
*dev_attr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
|
||||
struct dps_800ab_16_d_data *data = dps_800ab_16_d_update_device(dev);
|
||||
u8 *ptr = NULL;
|
||||
|
||||
if (!data->valid)
|
||||
return 0;
|
||||
|
||||
switch (attr->index) {
|
||||
case PSU_MFR_MODEL:
|
||||
ptr = data->mfr_model + 1;
|
||||
break;
|
||||
case PSU_MFR_SERIAL:
|
||||
ptr = data->mfr_serial + 1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return sprintf(buf, "%s\n", ptr);
|
||||
}
|
||||
static int dps_800ab_16_d_read_byte(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int dps_800ab_16_d_read_word(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
|
||||
static int dps_800ab_16_d_write_word(struct i2c_client *client, u8 reg, \
|
||||
u16 value)
|
||||
{
|
||||
union i2c_smbus_data data;
|
||||
data.word = value;
|
||||
return i2c_smbus_xfer(client->adapter, client->addr,
|
||||
client->flags |= I2C_CLIENT_PEC,
|
||||
I2C_SMBUS_WRITE, reg,
|
||||
I2C_SMBUS_WORD_DATA, &data);
|
||||
|
||||
}
|
||||
|
||||
static int dps_800ab_16_d_read_block(struct i2c_client *client, u8 command, \
|
||||
u8 *data, int data_len)
|
||||
{
|
||||
int result = i2c_smbus_read_i2c_block_data(client, command, data_len,
|
||||
data);
|
||||
if (unlikely(result < 0))
|
||||
goto abort;
|
||||
if (unlikely(result != data_len)) {
|
||||
result = -EIO;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
abort:
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
struct reg_data_byte {
|
||||
u8 reg;
|
||||
u8 *value;
|
||||
};
|
||||
|
||||
struct reg_data_word {
|
||||
u8 reg;
|
||||
u16 *value;
|
||||
};
|
||||
|
||||
static struct dps_800ab_16_d_data *dps_800ab_16_d_update_device( \
|
||||
struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct dps_800ab_16_d_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
/* Select SWPLD PSU offset */
|
||||
|
||||
if (time_after(jiffies, data->last_updated)) {
|
||||
int i, status;
|
||||
u8 command;
|
||||
struct reg_data_byte regs_byte[] = {
|
||||
{0x20, &data->vout_mode},
|
||||
{0x81, &data->fan_target}
|
||||
};
|
||||
struct reg_data_word regs_word[] = {
|
||||
{0x88, &data->in1_input},
|
||||
{0x8b, &data->in2_input},
|
||||
{0x89, &data->curr1_input},
|
||||
{0x8c, &data->curr2_input},
|
||||
{0x97, &data->power1_input},
|
||||
{0x96, &data->power2_input},
|
||||
{0x8d, &(data->temp_input[0])},
|
||||
{0x8e, &(data->temp_input[1])},
|
||||
{0x3b, &(data->fan_duty_cycle_input[0])},
|
||||
{0x90, &(data->fan_speed_input[0])},
|
||||
};
|
||||
|
||||
dev_dbg(&client->dev, "start data update\n");
|
||||
/* one milliseconds from now */
|
||||
data->last_updated = jiffies + HZ / 1000;
|
||||
|
||||
data->in1_input = 0;
|
||||
data->in2_input = 0;
|
||||
data->curr1_input = 0;
|
||||
data->curr2_input = 0;
|
||||
data->power1_input = 0;
|
||||
data->power2_input = 0;
|
||||
data->temp_input[0] = 0;
|
||||
data->temp_input[1] = 0;
|
||||
data->fan_duty_cycle_input[0] = 0;
|
||||
data->fan_speed_input[0] = 0;
|
||||
|
||||
data->mfr_model[0] = '\0';
|
||||
data->mfr_serial[0] = '\0';
|
||||
|
||||
command = 0x9a; /* PSU mfr_model */
|
||||
status = dps_800ab_16_d_read_block(client, command,
|
||||
data->mfr_model, ARRAY_SIZE(data->mfr_model) - 1);
|
||||
data->mfr_model[ARRAY_SIZE(data->mfr_model) - 1] = '\0';
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command,
|
||||
status);
|
||||
}
|
||||
|
||||
command = 0x9e; /* PSU mfr_serial */
|
||||
status = dps_800ab_16_d_read_block(client, command,
|
||||
data->mfr_serial, ARRAY_SIZE(data->mfr_serial) - 1);
|
||||
data->mfr_serial[ARRAY_SIZE(data->mfr_serial) - 1] = '\0';
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command,
|
||||
status);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs_byte); i++) {
|
||||
status = dps_800ab_16_d_read_byte(client,
|
||||
regs_byte[i].reg);
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_byte[i].reg, status);
|
||||
} else {
|
||||
*(regs_byte[i].value) = status;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(regs_word); i++) {
|
||||
status = dps_800ab_16_d_read_word(client,
|
||||
regs_word[i].reg);
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n",
|
||||
regs_word[i].reg, status);
|
||||
} else {
|
||||
*(regs_word[i].value) = status;
|
||||
}
|
||||
}
|
||||
/*
|
||||
command = 0x9a; / PSU mfr_model /
|
||||
status = dps_800ab_16_d_read_block(client, command,
|
||||
data->mfr_model, ARRAY_SIZE(data->mfr_model) - 1);
|
||||
data->mfr_model[ARRAY_SIZE(data->mfr_model) - 1] = '\0';
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command,
|
||||
status);
|
||||
}
|
||||
|
||||
command = 0x9e; / PSU mfr_serial /
|
||||
status = dps_800ab_16_d_read_block(client, command,
|
||||
data->mfr_serial, ARRAY_SIZE(data->mfr_serial) - 1);
|
||||
data->mfr_serial[ARRAY_SIZE(data->mfr_serial) - 1] = '\0';
|
||||
if (status < 0) {
|
||||
dev_dbg(&client->dev, "reg %d, err %d\n", command,
|
||||
status);
|
||||
}
|
||||
*/
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/* sysfs attributes for hwmon */
|
||||
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, for_linear_data, NULL, PSU_V_IN);
|
||||
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, for_vout_data, NULL, PSU_V_OUT);
|
||||
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, for_linear_data, NULL, PSU_I_IN);
|
||||
static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, for_linear_data, NULL, PSU_I_OUT);
|
||||
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, for_linear_data, NULL, PSU_P_IN);
|
||||
static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, for_linear_data, NULL, PSU_P_OUT);
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, \
|
||||
S_IRUGO, for_linear_data, NULL, PSU_TEMP1_INPUT);
|
||||
static SENSOR_DEVICE_ATTR(fan1_target, \
|
||||
S_IRUGO, for_fan_target, NULL, PSU_FAN1_FAULT);
|
||||
static SENSOR_DEVICE_ATTR(fan1_set_percentage, S_IWUGO | S_IRUGO, \
|
||||
for_linear_data, set_fan_duty_cycle_input, PSU_FAN1_DUTY_CYCLE);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, \
|
||||
S_IRUGO, for_linear_data, NULL, PSU_FAN1_SPEED);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_model, \
|
||||
S_IRUGO, for_ascii, NULL, PSU_MFR_MODEL);
|
||||
static SENSOR_DEVICE_ATTR(psu_mfr_serial, \
|
||||
S_IRUGO, for_ascii, NULL, PSU_MFR_SERIAL);
|
||||
/*
|
||||
static SENSOR_DEVICE_ATTR(psu_select_member, S_IWUGO | S_IRUGO, \
|
||||
for_r_member_data, set_w_member_data, PSU_SELECT_MEMBER);
|
||||
*/
|
||||
static struct attribute *dps_800ab_16_d_attributes[] = {
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_curr2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_power2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_target.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_set_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_model.dev_attr.attr,
|
||||
&sensor_dev_attr_psu_mfr_serial.dev_attr.attr,
|
||||
//&sensor_dev_attr_psu_select_member.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group dps_800ab_16_d_group = {
|
||||
.attrs = dps_800ab_16_d_attributes,
|
||||
};
|
||||
|
||||
static int dps_800ab_16_d_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct dps_800ab_16_d_data *data;
|
||||
int status;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
status = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
status = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
data->valid = 0;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
dev_info(&client->dev, "new chip found\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
status = sysfs_create_group(&client->dev.kobj, &dps_800ab_16_d_group);
|
||||
if (status)
|
||||
goto exit_sysfs_create_group;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
status = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_hwmon_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_hwmon_device_register:
|
||||
sysfs_remove_group(&client->dev.kobj, &dps_800ab_16_d_group);
|
||||
exit_sysfs_create_group:
|
||||
kfree(data);
|
||||
exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int dps_800ab_16_d_remove(struct i2c_client *client)
|
||||
{
|
||||
struct dps_800ab_16_d_data *data = i2c_get_clientdata(client);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &dps_800ab_16_d_group);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum id_name {
|
||||
dni_ag5648_psu,
|
||||
dps_800ab_16_d
|
||||
};
|
||||
|
||||
static const struct i2c_device_id dps_800ab_16_d_id[] = {
|
||||
{ "dni_ag5648_psu", dni_ag5648_psu },
|
||||
{ "dps_800ab_16_d", dps_800ab_16_d },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dps_800ab_16_d_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver dps_800ab_16_d_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "dps_800ab_16_d",
|
||||
},
|
||||
.probe = dps_800ab_16_d_probe,
|
||||
.remove = dps_800ab_16_d_remove,
|
||||
.id_table = dps_800ab_16_d_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static int __init dps_800ab_16_d_init(void)
|
||||
{
|
||||
return i2c_add_driver(&dps_800ab_16_d_driver);
|
||||
}
|
||||
|
||||
static void __exit dps_800ab_16_d_exit(void)
|
||||
{
|
||||
i2c_del_driver(&dps_800ab_16_d_driver);
|
||||
}
|
||||
|
||||
|
||||
MODULE_AUTHOR("Aries Lin <aries.lin@deltaww.com>");
|
||||
MODULE_DESCRIPTION("DPS_800AB_16_D Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(dps_800ab_16_d_init);
|
||||
module_exit(dps_800ab_16_d_exit);
|
@ -0,0 +1 @@
|
||||
../../common/modules/dni_emc2305.c
|
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
#platform init script for Delta ag5648
|
||||
|
||||
#fan speed monitor start
|
||||
/usr/share/sonic/device/x86_64-delta_ag5648-r0/fancontrol.service /usr/share/sonic/device/x86_64-delta_ag5648-r0/fancontrol &
|
||||
|
||||
#led control
|
||||
/usr/local/bin/led_control &
|
||||
|
||||
|
||||
exit 0
|
||||
|
221
platform/broadcom/sonic-platform-modules-delta/ag5648/scripts/led_control
Executable file
221
platform/broadcom/sonic-platform-modules-delta/ag5648/scripts/led_control
Executable file
@ -0,0 +1,221 @@
|
||||
#!/bin/bash
|
||||
|
||||
FAN1_EEPROM="-y 3 0x51 0x0a"
|
||||
FAN2_EEPROM="-y 3 0x52 0x0a"
|
||||
FAN3_EEPROM="-y 3 0x53 0x0a"
|
||||
FAN4_EEPROM="-y 3 0x54 0x0a"
|
||||
LED_CONTROL="/sys/devices/platform/delta-ag5648-cpld.0/led_control"
|
||||
|
||||
FAN1_REAR_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-3/3-004d/fan1_input"
|
||||
FAN2_REAR_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-3/3-004d/fan2_input"
|
||||
FAN3_REAR_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-3/3-004d/fan3_input"
|
||||
FAN4_REAR_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-3/3-004d/fan4_input"
|
||||
|
||||
FAN1_FRONT_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-5/5-004d/fan1_input"
|
||||
FAN2_FRONT_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-5/5-004d/fan2_input"
|
||||
FAN3_FRONT_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-5/5-004d/fan3_input"
|
||||
FAN4_FRONT_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-5/5-004d/fan4_input"
|
||||
|
||||
PSU1_EEPROM="-y 6 0x51 0x00"
|
||||
PSU2_EEPROM="-y 6 0x50 0x00"
|
||||
PSU1_FAN_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-6/6-0059/fan1_input"
|
||||
PSU2_FAN_RPM="/sys/devices/pci0000:00/0000:00:13.0/i2c-1/i2c-6/6-0058/fan1_input"
|
||||
|
||||
catfaneeprom(){
|
||||
fan_eeprom_num=0
|
||||
fan1_eeprom_num=0
|
||||
i2cget $FAN1_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
fan1_eeprom_num=$((fan1_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
fan1_eeprom_num=$((fan1_eeprom_num))
|
||||
fi
|
||||
|
||||
fan2_eeprom_num=0
|
||||
i2cget $FAN2_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
fan2_eeprom_num=$((fan2_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
fan2_eeprom_num=$((fan2_eeprom_num))
|
||||
fi
|
||||
|
||||
fan3_eeprom_num=0
|
||||
i2cget $FAN3_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
fan3_eeprom_num=$((fan3_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
fan3_eeprom_num=$((fan3_eeprom_num))
|
||||
fi
|
||||
|
||||
fan4_eeprom_num=0
|
||||
i2cget $FAN4_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
fan4_eeprom_num=$((fan4_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
fan4_eeprom_num=$((fan4_eeprom_num))
|
||||
fi
|
||||
|
||||
fan_eeprom_num=$((fan1_eeprom_num+fan2_eeprom_num+fan3_eeprom_num+fan4_eeprom_num))
|
||||
}
|
||||
|
||||
catfanspeed(){
|
||||
fan_rpm_normal_num=0
|
||||
fan1_rpm_normal_num=0
|
||||
fan1_front_rpm=`cat $FAN1_FRONT_RPM`
|
||||
fan1_rear_rpm=`cat $FAN1_REAR_RPM`
|
||||
if [ "${fan1_front_rpm}" -ne "960" ] && [ "${fan1_rear_rpm}" -ne "960" ] && [ "${fan1_front_rpm}" -ne "0" ] && [ "${fan1_rear_rpm}" -ne "0" ]; then
|
||||
fan1_rpm_normal_num=$((fan1_rpm_normal_num+1))
|
||||
elif [ "${fan1_front_rpm}" -eq "960" ] || [ "${fan1_rear_rpm}" -eq "960" ] || [ "${fan1_front_rpm}" -eq "0" ] || [ "${fan1_rear_rpm}" -eq "0" ]; then
|
||||
fan1_rpm_normal_num=$((fan1_rpm_normal_num))
|
||||
fi
|
||||
|
||||
fan2_rpm_normal_num=0
|
||||
fan2_front_rpm=`cat $FAN2_FRONT_RPM`
|
||||
fan2_rear_rpm=`cat $FAN2_REAR_RPM`
|
||||
if [ "${fan2_front_rpm}" -ne "960" ] && [ "${fan2_rear_rpm}" -ne "960" ] && [ "${fan2_front_rpm}" -ne "0" ] && [ "${fan2_rear_rpm}" -ne "0" ]; then
|
||||
fan2_rpm_normal_num=$((fan2_rpm_normal_num+1))
|
||||
elif [ "${fan2_front_rpm}" -eq "960" ] || [ "${fan2_rear_rpm}" -eq "960" ] || [ "${fan2_front_rpm}" -eq "0" ] || [ "${fan2_rear_rpm}" -eq "0" ]; then
|
||||
fan2_rpm_normal_num=$((fan2_rpm_normal_num))
|
||||
fi
|
||||
|
||||
fan3_rpm_normal_num=0
|
||||
fan3_front_rpm=`cat $FAN3_FRONT_RPM`
|
||||
fan3_rear_rpm=`cat $FAN3_REAR_RPM`
|
||||
if [ "${fan3_front_rpm}" -ne "960" ] && [ "${fan3_rear_rpm}" -ne "960" ] && [ "${fan3_front_rpm}" -ne "0" ] && [ "${fan3_rear_rpm}" -ne "0" ]; then
|
||||
fan3_rpm_normal_num=$((fan3_rpm_normal_num+1))
|
||||
elif [ "${fan3_front_rpm}" -eq "960" ] || [ "${fan3_rear_rpm}" -eq "960" ] || [ "${fan3_front_rpm}" -eq "0" ] || [ "${fan3_rear_rpm}" -eq "0" ]; then
|
||||
fan3_rpm_normal_num=$((fan3_rpm_normal_num))
|
||||
fi
|
||||
|
||||
fan4_rpm_normal_num=0
|
||||
fan4_front_rpm=`cat $FAN4_FRONT_RPM`
|
||||
fan4_rear_rpm=`cat $FAN4_REAR_RPM`
|
||||
if [ "${fan4_front_rpm}" -ne "960" ] && [ "${fan4_rear_rpm}" -ne "960" ] && [ "${fan4_front_rpm}" -ne "0" ] && [ "${fan4_rear_rpm}" -ne "0" ]; then
|
||||
fan4_rpm_normal_num=$((fan4_rpm_normal_num+1))
|
||||
elif [ "${fan4_front_rpm}" -eq "960" ] || [ "${fan4_rear_rpm}" -eq "960" ] || [ "${fan4_front_rpm}" -eq "0" ] || [ "${fan4_rear_rpm}" -eq "0" ]; then
|
||||
fan4_rpm_normal_num=$((fan4_rpm_normal_num))
|
||||
fi
|
||||
|
||||
fan_rpm_normal_num=$((fan1_rpm_normal_num+fan2_rpm_normal_num+fan3_rpm_normal_num+fan4_rpm_normal_num))
|
||||
|
||||
}
|
||||
|
||||
catpsueeprom(){
|
||||
psu1_eeprom_num=0
|
||||
i2cget $PSU1_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
psu1_eeprom_num=$((psu1_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
psu1_eeprom_num=$((psu1_eeprom_num))
|
||||
fi
|
||||
|
||||
psu2_eeprom_num=0
|
||||
i2cget $PSU2_EEPROM > /dev/null 2>&1
|
||||
if [ "`echo $?`" -eq "0" ]; then
|
||||
psu2_eeprom_num=$((psu2_eeprom_num+1))
|
||||
elif [ "`echo $?`" -eq "2" ]; then
|
||||
psu2_eeprom_num=$((psu2_eeprom_num))
|
||||
fi
|
||||
}
|
||||
|
||||
catpsufanspeed(){
|
||||
|
||||
psu1_rpm_normal_num=0
|
||||
psu1_rpm=`cat $PSU1_FAN_RPM`
|
||||
|
||||
if [ "${psu1_rpm}" -ne "960" ] && [ "${psu1_rpm}" -ne "0" ]; then
|
||||
psu1_rpm_normal_num=$((psu1_rpm_normal_num+1))
|
||||
elif [ "${psu1_rpm}" -eq "960" ] || [ "${psu1_rpm}" -eq "0" ]; then
|
||||
psu1_rpm_normal_num=$((psu1_rpm_normal_num))
|
||||
fi
|
||||
|
||||
psu2_rpm_normal_num=0
|
||||
psu2_rpm=`cat $PSU2_FAN_RPM`
|
||||
|
||||
if [ "${psu2_rpm}" -ne "960" ] && [ "${psu2_rpm}" -ne "0" ]; then
|
||||
psu2_rpm_normal_num=$((psu2_rpm_normal_num+1))
|
||||
elif [ "${psu2_rpm}" -eq "960" ] || [ "${psu2_rpm}" -eq "0" ]; then
|
||||
psu2_rpm_normal_num=$((psu2_rpm_normal_num))
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
setfanled(){
|
||||
if [ "${fan_eeprom_num}" -eq "4" ] && [ "${fan_rpm_normal_num}" -eq "4" ]; then
|
||||
echo "fan_green" > $LED_CONTROL
|
||||
elif [ "${fan_eeprom_num}" -lt "4" ] && [ "${fan_rpm_normal_num}" -eq "4" ]; then
|
||||
echo "fan_Amber" > $LED_CONTROL
|
||||
elif [ "${psu1_eeprom_num}" -eq "0" ] && [ "${psu2_eeprom_num}" -eq "0" ]; then
|
||||
echo "fan_off" > $LED_CONTROL
|
||||
else
|
||||
echo "fan_Blinking_yellow" > $LED_CONTROL
|
||||
fi
|
||||
}
|
||||
|
||||
setpsuled(){
|
||||
if [ "${psu1_rpm_normal_num}" -eq "1" ] && [ "${psu2_rpm_normal_num}" -eq "1" ]; then
|
||||
echo "pwr_green" > $LED_CONTROL
|
||||
elif [ "${psu1_eeprom_num}" -eq "1" ] && [ "${psu2_eeprom_num}" -eq "1" ]; then
|
||||
echo "pwr_Blinking_Amber" > $LED_CONTROL
|
||||
elif [ "${psu1_eeprom_num}" -eq "0" ] || [ "${psu2_eeprom_num}" -eq "0" ]; then
|
||||
echo "pwr_Amber" > $LED_CONTROL
|
||||
else
|
||||
echo "pwr_off" > $LED_CONTROL
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
setfantrayled(){
|
||||
if [ "${fan1_rpm_normal_num}" -eq "1" ]; then
|
||||
echo "fan1_green" > $LED_CONTROL
|
||||
elif [ "${fan1_eeprom_num}" -eq "1" ]; then
|
||||
echo "fan1_Amber" > $LED_CONTROL
|
||||
else
|
||||
echo "fan1_off" > $LED_CONTROL
|
||||
fi
|
||||
|
||||
if [ "${fan2_rpm_normal_num}" -eq "1" ]; then
|
||||
echo "fan2_green" > $LED_CONTROL
|
||||
elif [ "${fan2_eeprom_num}" -eq "1" ]; then
|
||||
echo "fan2_Amber" > $LED_CONTROL
|
||||
else
|
||||
echo "fan2_off" > $LED_CONTROL
|
||||
fi
|
||||
|
||||
if [ "${fan3_rpm_normal_num}" -eq "1" ]; then
|
||||
echo "fan3_green" > $LED_CONTROL
|
||||
elif [ "${fan3_eeprom_num}" -eq "1" ]; then
|
||||
echo "fan3_Amber" > $LED_CONTROL
|
||||
else
|
||||
echo "fan3_off" > $LED_CONTROL
|
||||
fi
|
||||
|
||||
if [ "${fan4_rpm_normal_num}" -eq "1" ]; then
|
||||
echo "fan4_green" > $LED_CONTROL
|
||||
elif [ "${fan4_eeprom_num}" -eq "1" ]; then
|
||||
echo "fan4_Amber" > $LED_CONTROL
|
||||
else
|
||||
echo "fan4_off" > $LED_CONTROL
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
platformstatus(){
|
||||
|
||||
echo "sys_green" > $LED_CONTROL
|
||||
catfaneeprom
|
||||
catfanspeed
|
||||
setfanled
|
||||
setfantrayled
|
||||
|
||||
catpsueeprom
|
||||
catpsufanspeed
|
||||
setpsuled
|
||||
}
|
||||
|
||||
while true
|
||||
do
|
||||
platformstatus
|
||||
sleep 5
|
||||
done
|
||||
|
@ -0,0 +1,94 @@
|
||||
# libsensors configuration file for AG5648
|
||||
# ------------------------------------------------
|
||||
#
|
||||
|
||||
bus "i2c-2" "i2c-1-mux (chan_id 0)"
|
||||
bus "i2c-3" "i2c-1-mux (chan_id 1)"
|
||||
bus "i2c-4" "i2c-1-mux (chan_id 2)"
|
||||
bus "i2c-5" "i2c-1-mux (chan_id 3)"
|
||||
bus "i2c-6" "i2c-1-mux (chan_id 4)"
|
||||
bus "i2c-7" "i2c-1-mux (chan_id 5)"
|
||||
|
||||
# tmp75-i2c-2-4d board sensor near Left of front vents.
|
||||
# tmp75-i2c-3-49 board sensor near MAC.
|
||||
# tmp75-i2c-3-4b board sensor near Middle of front vents.
|
||||
# tmp75-i2c-3-4c board sensor near Right of front vents.
|
||||
# tmp75-i2c-3-4e board sensor near DC fan.
|
||||
# tmp75-i2c-3-4f board sensor near CPU.
|
||||
|
||||
chip "tmp75-i2c-*-4d"
|
||||
label temp1 "board sensor near Left of front vents"
|
||||
set temp1_max 60
|
||||
set temp1_max_hyst 55
|
||||
|
||||
chip "tmp75-i2c-*-49"
|
||||
label temp1 "board sensor near MAC"
|
||||
set temp1_max 85
|
||||
set temp1_max_hyst 80
|
||||
|
||||
chip "tmp75-i2c-*-4b"
|
||||
label temp1 "board sensor near Middle of front vents"
|
||||
set temp1_max 70
|
||||
set temp1_max_hyst 65
|
||||
|
||||
chip "tmp75-i2c-*-4c"
|
||||
label temp1 "board sensor near Right of front vents"
|
||||
set temp1_max 65
|
||||
set temp1_max_hyst 60
|
||||
|
||||
chip "tmp75-i2c-*-4e"
|
||||
label temp1 "board sensor near DC fan"
|
||||
set temp1_max 60
|
||||
set temp1_max_hyst 55
|
||||
|
||||
chip "tmp75-i2c-*-4f"
|
||||
label temp1 "board sensor near CPU"
|
||||
set temp1_max 80
|
||||
set temp1_max_hyst 75
|
||||
|
||||
chip "emc2305-i2c-3-4d"
|
||||
label fan1 "FANTRAY 1 REAR"
|
||||
label fan2 "FANTRAY 2 REAR"
|
||||
label fan3 "FANTRAY 3 REAR"
|
||||
label fan4 "FANTRAY 4 REAR"
|
||||
label fan5 "FANTRAY 5 REAR"
|
||||
|
||||
chip "emc2305-i2c-5-4d"
|
||||
label fan1 "FANTRAY 1 FRONT"
|
||||
label fan2 "FANTRAY 2 FRONT"
|
||||
label fan3 "FANTRAY 3 FRONT"
|
||||
label fan4 "FANTRAY 4 FRONT"
|
||||
label fan5 "FANTRAY 5 FRONT"
|
||||
|
||||
chip "ltc4215-i2c-*-40"
|
||||
label in1 "PSU Hot-Swap voltage 1"
|
||||
label in2 "PSU Hot-Swap voltage 2"
|
||||
label power1 "PSU Hot-Swap power"
|
||||
label curr1 "PSU Hot-Swap current"
|
||||
|
||||
chip "ltc4215-i2c-*-42"
|
||||
label in1 "PSU Hot-Swap voltage 1"
|
||||
label in2 "PSU Hot-Swap voltage 2"
|
||||
label power1 "PSU Hot-Swap power"
|
||||
label curr1 "PSU Hot-Swap current"
|
||||
|
||||
chip "dni_ag5648_psu-i2c-*-59"
|
||||
label in1 "PSU voltage 1"
|
||||
label in2 "PSU voltage 2"
|
||||
label fan1 "PSU fan"
|
||||
label temp1 "PSU temperature"
|
||||
label power1 "PSU power1"
|
||||
label power2 "PSU power2"
|
||||
label curr1 "PSU current1"
|
||||
label curr2 "PSU current2"
|
||||
|
||||
chip "dni_ag5648_psu-i2c-*-58"
|
||||
label in1 "PSU voltage 1"
|
||||
label in2 "PSU voltage 2"
|
||||
label fan1 "PSU fan"
|
||||
label temp1 "PSU temperature"
|
||||
label power1 "PSU power1"
|
||||
label power2 "PSU power2"
|
||||
label curr1 "PSU current1"
|
||||
label curr2 "PSU current2"
|
||||
|
@ -1,2 +1,2 @@
|
||||
obj-m := at24.o dni_ag9032v1_psu.o dni_emc2305.o delta_ag9032v1_platform.o delta_ag9032v1_cpupld.o
|
||||
obj-m := dni_ag9032v1_psu.o dni_emc2305.o delta_ag9032v1_platform.o delta_ag9032v1_cpupld.o
|
||||
|
||||
|
@ -1,698 +0,0 @@
|
||||
/*
|
||||
* at24.c - handle most I2C EEPROMs
|
||||
*
|
||||
* Copyright (C) 2005-2007 David Brownell
|
||||
* Copyright (C) 2008 Wolfram Sang, Pengutronix
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/at24.h>
|
||||
|
||||
/*
|
||||
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
|
||||
* Differences between different vendor product lines (like Atmel AT24C or
|
||||
* MicroChip 24LC, etc) won't much matter for typical read/write access.
|
||||
* There are also I2C RAM chips, likewise interchangeable. One example
|
||||
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
|
||||
*
|
||||
* However, misconfiguration can lose data. "Set 16-bit memory address"
|
||||
* to a part with 8-bit addressing will overwrite data. Writing with too
|
||||
* big a page size also loses data. And it's not safe to assume that the
|
||||
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
|
||||
* uses 0x51, for just one example.
|
||||
*
|
||||
* Accordingly, explicit board-specific configuration data should be used
|
||||
* in almost all cases. (One partial exception is an SMBus used to access
|
||||
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
|
||||
*
|
||||
* So this driver uses "new style" I2C driver binding, expecting to be
|
||||
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
|
||||
* similar kernel-resident tables; or, configuration data coming from
|
||||
* a bootloader.
|
||||
*
|
||||
* Other than binding model, current differences from "eeprom" driver are
|
||||
* that this one handles write access and isn't restricted to 24c02 devices.
|
||||
* It also handles larger devices (32 kbit and up) with two-byte addresses,
|
||||
* which won't work on pure SMBus systems.
|
||||
*/
|
||||
|
||||
struct at24_data {
|
||||
struct at24_platform_data chip;
|
||||
struct memory_accessor macc;
|
||||
int use_smbus;
|
||||
|
||||
/*
|
||||
* Lock protects against activities from other Linux tasks,
|
||||
* but not from changes by other I2C masters.
|
||||
*/
|
||||
struct mutex lock;
|
||||
struct bin_attribute bin;
|
||||
|
||||
u8 *writebuf;
|
||||
unsigned write_max;
|
||||
unsigned num_addresses;
|
||||
|
||||
/*
|
||||
* Some chips tie up multiple I2C addresses; dummy devices reserve
|
||||
* them for us, and we'll use them with SMBus calls.
|
||||
*/
|
||||
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 unsigned io_limit = 32;
|
||||
module_param(io_limit, uint, 0);
|
||||
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 32)");
|
||||
|
||||
/*
|
||||
* Specs often allow 5 msec for a page write, sometimes 20 msec;
|
||||
* it's important to recover from write timeouts.
|
||||
*/
|
||||
static unsigned write_timeout = 25;
|
||||
module_param(write_timeout, uint, 0);
|
||||
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
|
||||
|
||||
#define AT24_SIZE_BYTELEN 5
|
||||
#define AT24_SIZE_FLAGS 8
|
||||
|
||||
#define AT24_BITMASK(x) (BIT(x) - 1)
|
||||
|
||||
/* create non-zero magic value for given eeprom parameters */
|
||||
#define AT24_DEVICE_MAGIC(_len, _flags) \
|
||||
((1 << AT24_SIZE_FLAGS | (_flags)) \
|
||||
<< AT24_SIZE_BYTELEN | ilog2(_len))
|
||||
|
||||
static const struct i2c_device_id at24_ids[] = {
|
||||
/* needs 8 addresses as A0-A2 are ignored */
|
||||
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
|
||||
/* old variants can't be handled with this generic entry! */
|
||||
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
|
||||
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
|
||||
/* spd is a 24c02 in memory DIMMs */
|
||||
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
|
||||
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
|
||||
/* 24rf08 quirk is handled at i2c-core */
|
||||
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
|
||||
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
|
||||
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "at24", 0 },
|
||||
{ /* END OF LIST */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, at24_ids);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This routine supports chips which consume multiple I2C addresses. It
|
||||
* computes the addressing information to be used for a given r/w request.
|
||||
* Assumes that sanity checks for offset happened at sysfs-layer.
|
||||
*/
|
||||
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
|
||||
unsigned *offset)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16) {
|
||||
i = *offset >> 16;
|
||||
*offset &= 0xffff;
|
||||
} else {
|
||||
i = *offset >> 8;
|
||||
*offset &= 0xff;
|
||||
}
|
||||
|
||||
return at24->client[i];
|
||||
}
|
||||
|
||||
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
||||
unsigned offset, size_t count)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
u8 msgbuf[2];
|
||||
struct i2c_client *client;
|
||||
unsigned long timeout, read_time;
|
||||
int status, i;
|
||||
|
||||
memset(msg, 0, sizeof(msg));
|
||||
|
||||
/*
|
||||
* REVISIT some multi-address chips don't rollover page reads to
|
||||
* the next slave address, so we may need to truncate the count.
|
||||
* Those chips might need another quirk flag.
|
||||
*
|
||||
* If the real hardware used four adjacent 24c02 chips and that
|
||||
* were misconfigured as one 24c08, that would be a similar effect:
|
||||
* one "eeprom" file not four, but larger reads would fail when
|
||||
* they crossed certain pages.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Slave address and byte offset derive from the offset. Always
|
||||
* set the byte address; on a multi-master board, another master
|
||||
* may have changed the chip's "current" address pointer.
|
||||
*/
|
||||
client = at24_translate_offset(at24, &offset);
|
||||
|
||||
if (count > io_limit)
|
||||
count = io_limit;
|
||||
|
||||
switch (at24->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. Note that read page rollover helps us
|
||||
* here (unlike writes). msgbuf is u8 and will cast to our
|
||||
* needs.
|
||||
*/
|
||||
i = 0;
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||
msgbuf[i++] = offset >> 8;
|
||||
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 (at24->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, "read %zu@%d --> %d (%ld)\n",
|
||||
count, offset, status, jiffies);
|
||||
|
||||
if (status == count)
|
||||
return count;
|
||||
|
||||
/* REVISIT: at HZ=100, this is sloooow */
|
||||
msleep(1);
|
||||
} while (time_before(read_time, timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static ssize_t at24_read(struct at24_data *at24,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (unlikely(!count))
|
||||
return count;
|
||||
|
||||
/*
|
||||
* Read data from chip, protecting against concurrent updates
|
||||
* from this host, but not from other I2C masters.
|
||||
*/
|
||||
mutex_lock(&at24->lock);
|
||||
|
||||
while (count) {
|
||||
ssize_t status;
|
||||
|
||||
status = at24_eeprom_read(at24, buf, off, count);
|
||||
if (status <= 0) {
|
||||
if (retval == 0)
|
||||
retval = status;
|
||||
break;
|
||||
}
|
||||
buf += status;
|
||||
off += status;
|
||||
count -= status;
|
||||
retval += status;
|
||||
}
|
||||
|
||||
mutex_unlock(&at24->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct at24_data *at24;
|
||||
|
||||
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
|
||||
return at24_read(at24, buf, off, count);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Note that if the hardware write-protect pin is pulled high, the whole
|
||||
* chip is normally write protected. But there are plenty of product
|
||||
* variants here, including OTP fuses and partial chip protect.
|
||||
*
|
||||
* We only use page mode writes; the alternative is sloooow. This routine
|
||||
* writes at most one page.
|
||||
*/
|
||||
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
||||
unsigned offset, size_t count)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
struct i2c_msg msg;
|
||||
ssize_t status;
|
||||
unsigned long timeout, write_time;
|
||||
unsigned next_page;
|
||||
|
||||
/* Get corresponding I2C address and adjust offset */
|
||||
client = at24_translate_offset(at24, &offset);
|
||||
|
||||
/* write_max is at most a page */
|
||||
if (count > at24->write_max)
|
||||
count = at24->write_max;
|
||||
|
||||
/* Never roll over backwards, to the start of this page */
|
||||
next_page = roundup(offset + 1, at24->chip.page_size);
|
||||
if (offset + count > next_page)
|
||||
count = next_page - offset;
|
||||
|
||||
/* If we'll use I2C calls for I/O, set up the message */
|
||||
if (!at24->use_smbus) {
|
||||
int i = 0;
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = 0;
|
||||
|
||||
/* msg.buf is u8 and casts will mask the values */
|
||||
msg.buf = at24->writebuf;
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||
msg.buf[i++] = offset >> 8;
|
||||
|
||||
msg.buf[i++] = offset;
|
||||
memcpy(&msg.buf[i], buf, count);
|
||||
msg.len = i + count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes fail if the previous one 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;
|
||||
if (at24->use_smbus) {
|
||||
status = i2c_smbus_write_i2c_block_data(client,
|
||||
offset, count, buf);
|
||||
if (status == 0)
|
||||
status = count;
|
||||
} else {
|
||||
status = i2c_transfer(client->adapter, &msg, 1);
|
||||
if (status == 1)
|
||||
status = count;
|
||||
}
|
||||
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
|
||||
count, offset, status, jiffies);
|
||||
|
||||
if (status == count)
|
||||
return count;
|
||||
|
||||
/* REVISIT: at HZ=100, this is sloooow */
|
||||
msleep(1);
|
||||
} while (time_before(write_time, timeout));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (unlikely(!count))
|
||||
return count;
|
||||
|
||||
/*
|
||||
* Write data to chip, protecting against concurrent updates
|
||||
* from this host, but not from other I2C masters.
|
||||
*/
|
||||
mutex_lock(&at24->lock);
|
||||
|
||||
while (count) {
|
||||
ssize_t status;
|
||||
|
||||
status = at24_eeprom_write(at24, buf, off, count);
|
||||
if (status <= 0) {
|
||||
if (retval == 0)
|
||||
retval = status;
|
||||
break;
|
||||
}
|
||||
buf += status;
|
||||
off += status;
|
||||
count -= status;
|
||||
retval += status;
|
||||
}
|
||||
|
||||
mutex_unlock(&at24->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct at24_data *at24;
|
||||
|
||||
if (unlikely(off >= attr->size))
|
||||
return -EFBIG;
|
||||
|
||||
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
|
||||
return at24_write(at24, buf, off, count);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This lets other kernel code access the eeprom data. For example, it
|
||||
* might hold a board's Ethernet address, or board-specific calibration
|
||||
* data generated on the manufacturing floor.
|
||||
*/
|
||||
|
||||
static ssize_t at24_macc_read(struct memory_accessor *macc, char *buf,
|
||||
off_t offset, size_t count)
|
||||
{
|
||||
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
|
||||
|
||||
return at24_read(at24, buf, offset, count);
|
||||
}
|
||||
|
||||
static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
|
||||
off_t offset, size_t count)
|
||||
{
|
||||
struct at24_data *at24 = container_of(macc, struct at24_data, macc);
|
||||
|
||||
return at24_write(at24, buf, offset, count);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static void at24_get_ofdata(struct i2c_client *client,
|
||||
struct at24_platform_data *chip)
|
||||
{
|
||||
const __be32 *val;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
|
||||
if (node) {
|
||||
if (of_get_property(node, "read-only", NULL))
|
||||
chip->flags |= AT24_FLAG_READONLY;
|
||||
val = of_get_property(node, "pagesize", NULL);
|
||||
if (val)
|
||||
chip->page_size = be32_to_cpup(val);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void at24_get_ofdata(struct i2c_client *client,
|
||||
struct at24_platform_data *chip)
|
||||
{ }
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct at24_platform_data chip;
|
||||
bool writable;
|
||||
int use_smbus = 0;
|
||||
struct at24_data *at24;
|
||||
int err;
|
||||
unsigned i, num_addresses;
|
||||
kernel_ulong_t magic;
|
||||
|
||||
if (client->dev.platform_data) {
|
||||
chip = *(struct at24_platform_data *)client->dev.platform_data;
|
||||
} else {
|
||||
if (!id->driver_data)
|
||||
return -ENODEV;
|
||||
|
||||
magic = id->driver_data;
|
||||
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
|
||||
magic >>= AT24_SIZE_BYTELEN;
|
||||
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
|
||||
/*
|
||||
* This is slow, but we can't know all eeproms, so we better
|
||||
* play safe. Specifying custom eeprom-types via platform_data
|
||||
* is recommended anyhow.
|
||||
*/
|
||||
chip.page_size = 1;
|
||||
|
||||
/* update chipdata if OF is present */
|
||||
at24_get_ofdata(client, &chip);
|
||||
|
||||
chip.setup = NULL;
|
||||
chip.context = NULL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(chip.byte_len))
|
||||
dev_warn(&client->dev,
|
||||
"byte_len looks suspicious (no power of 2)!\n");
|
||||
if (!chip.page_size) {
|
||||
dev_err(&client->dev, "page_size must not be 0!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!is_power_of_2(chip.page_size))
|
||||
dev_warn(&client->dev,
|
||||
"page_size looks suspicious (no power of 2)!\n");
|
||||
|
||||
/* Use I2C operations unless we're stuck with SMBus extensions. */
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
if (chip.flags & AT24_FLAG_ADDR16)
|
||||
return -EPFNOSUPPORT;
|
||||
|
||||
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 {
|
||||
return -EPFNOSUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (chip.flags & AT24_FLAG_TAKE8ADDR)
|
||||
num_addresses = 8;
|
||||
else
|
||||
num_addresses = DIV_ROUND_UP(chip.byte_len,
|
||||
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
|
||||
|
||||
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
|
||||
num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
|
||||
if (!at24)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&at24->lock);
|
||||
at24->use_smbus = use_smbus;
|
||||
at24->chip = chip;
|
||||
at24->num_addresses = num_addresses;
|
||||
|
||||
/*
|
||||
* 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(&at24->bin);
|
||||
at24->bin.attr.name = "eeprom";
|
||||
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
|
||||
at24->bin.read = at24_bin_read;
|
||||
at24->bin.size = chip.byte_len;
|
||||
|
||||
at24->macc.read = at24_macc_read;
|
||||
|
||||
writable = !(chip.flags & AT24_FLAG_READONLY);
|
||||
if (writable) {
|
||||
if (!use_smbus || i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
||||
|
||||
unsigned write_max = chip.page_size;
|
||||
|
||||
at24->macc.write = at24_macc_write;
|
||||
|
||||
at24->bin.write = at24_bin_write;
|
||||
at24->bin.attr.mode |= S_IWUSR;
|
||||
|
||||
if (write_max > io_limit)
|
||||
write_max = io_limit;
|
||||
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
|
||||
write_max = I2C_SMBUS_BLOCK_MAX;
|
||||
at24->write_max = write_max;
|
||||
|
||||
/* buffer (data + address at the beginning) */
|
||||
at24->writebuf = devm_kzalloc(&client->dev,
|
||||
write_max + 2, GFP_KERNEL);
|
||||
if (!at24->writebuf)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
dev_warn(&client->dev,
|
||||
"cannot write due to controller restrictions.");
|
||||
}
|
||||
}
|
||||
|
||||
at24->client[0] = client;
|
||||
|
||||
/* use dummy devices for multiple-address chips */
|
||||
for (i = 1; i < num_addresses; i++) {
|
||||
at24->client[i] = i2c_new_dummy(client->adapter,
|
||||
client->addr + i);
|
||||
if (!at24->client[i]) {
|
||||
dev_err(&client->dev, "address 0x%02x unavailable\n",
|
||||
client->addr + i);
|
||||
err = -EADDRINUSE;
|
||||
goto err_clients;
|
||||
}
|
||||
}
|
||||
|
||||
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
|
||||
if (err)
|
||||
goto err_clients;
|
||||
|
||||
i2c_set_clientdata(client, at24);
|
||||
|
||||
dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
|
||||
at24->bin.size, client->name,
|
||||
writable ? "writable" : "read-only", at24->write_max);
|
||||
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");
|
||||
}
|
||||
|
||||
/* export data to kernel code */
|
||||
if (chip.setup)
|
||||
chip.setup(&at24->macc, chip.context);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clients:
|
||||
for (i = 1; i < num_addresses; i++)
|
||||
if (at24->client[i])
|
||||
i2c_unregister_device(at24->client[i]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int at24_remove(struct i2c_client *client)
|
||||
{
|
||||
struct at24_data *at24;
|
||||
int i;
|
||||
|
||||
at24 = i2c_get_clientdata(client);
|
||||
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
|
||||
|
||||
for (i = 1; i < at24->num_addresses; i++)
|
||||
i2c_unregister_device(at24->client[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct i2c_driver at24_driver = {
|
||||
.driver = {
|
||||
.name = "at24",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = at24_probe,
|
||||
.remove = at24_remove,
|
||||
.id_table = at24_ids,
|
||||
};
|
||||
|
||||
static int __init at24_init(void)
|
||||
{
|
||||
if (!io_limit) {
|
||||
pr_err("at24: io_limit must not be 0!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
io_limit = rounddown_pow_of_two(io_limit);
|
||||
return i2c_add_driver(&at24_driver);
|
||||
}
|
||||
module_init(at24_init);
|
||||
|
||||
static void __exit at24_exit(void)
|
||||
{
|
||||
i2c_del_driver(&at24_driver);
|
||||
}
|
||||
module_exit(at24_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
|
||||
MODULE_AUTHOR("David Brownell and Wolfram Sang");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,381 +0,0 @@
|
||||
/*
|
||||
* <bsn.cl fy=2013 v=gpl>
|
||||
*
|
||||
* Copyright (C) 2017 Delta Networks, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it
|
||||
* and/or modify it under the terms ofthe GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the License,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* </bsn.cl>
|
||||
*
|
||||
*
|
||||
* A hwmon driver for the SMSC EMC2305 fan controller
|
||||
* Complete datasheet is available (6/2013) at:
|
||||
* http://www.smsc.com/media/Downloads_Public/Data_Sheets/2305.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
|
||||
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf);
|
||||
static ssize_t set_fan(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf);
|
||||
static ssize_t set_fan_percentage(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_fan_percentage(struct device *dev, struct device_attribute * devattr,
|
||||
char *buf);
|
||||
static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, 0x2F, 0x4C,
|
||||
0x4D, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
|
||||
#define EMC2305_REG_DEVICE 0xFD
|
||||
#define EMC2305_REG_VENDOR 0xFE
|
||||
|
||||
//#define FAN_MINIMUN 0x33 /*20%*/
|
||||
#define FAN_MINIMUN 0x0 /*0%*/
|
||||
#define FAN_RPM_BASED 0xAB
|
||||
|
||||
#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_TACH(n) (0x3E + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_CONF(n) (0x32 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_REAR_H_RPM(n) (0x3D + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_REAR_L_RPM(n) (0x3C + 0x10 * n)
|
||||
|
||||
#define EMC2305_DEVICE 0x34
|
||||
#define EMC2305_VENDOR 0x5D
|
||||
#define MAX_FAN_SPEED 23000
|
||||
|
||||
struct emc2305_data
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
struct attribute_group attrs;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int emc2305_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int emc2305_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info);
|
||||
static int emc2305_remove(struct i2c_client *client);
|
||||
|
||||
static const struct i2c_device_id emc2305_id[] =
|
||||
{
|
||||
{ "emc2305", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc2305_id);
|
||||
|
||||
static struct i2c_driver emc2305_driver =
|
||||
{
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "emc2305",
|
||||
},
|
||||
.probe = emc2305_probe,
|
||||
.remove = emc2305_remove,
|
||||
.id_table = emc2305_id,
|
||||
.detect = emc2305_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 4);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 4);
|
||||
|
||||
static struct attribute *emc2305_attr[] =
|
||||
{
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm4.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t show_fan_percentage(struct device *dev, struct device_attribute * devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_word_swapped(client,
|
||||
EMC2305_REG_FAN_TACH(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
/* Left shift 3 bits for showing correct RPM */
|
||||
val = val >> 3;
|
||||
if ((int)(3932160 * 2 / (val > 0 ? val : 1) == 960))return sprintf(buf, "%d\n", 0);
|
||||
return sprintf(buf, "%d\n", (int)(3932160 * 2 / (val > 0 ? val : 1) * 100 / MAX_FAN_SPEED));
|
||||
}
|
||||
|
||||
|
||||
static ssize_t set_fan_percentage(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long hsb, lsb;
|
||||
unsigned long tech;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 100)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val <= 5)
|
||||
{
|
||||
hsb = 0xff; /*high bit*/
|
||||
lsb = 0xe0; /*low bit*/
|
||||
}
|
||||
else
|
||||
{
|
||||
val = val * 230;
|
||||
tech = (3932160 * 2) / (val > 0 ? val : 1);
|
||||
hsb = (uint8_t)(((tech << 3) >> 8) & 0x0ff);
|
||||
lsb = (uint8_t)((tech << 3) & 0x0f8);
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_H_RPM(attr->index), hsb);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_L_RPM(attr->index), lsb);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_word_swapped(client,
|
||||
EMC2305_REG_FAN_TACH(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
/* Left shift 3 bits for showing correct RPM */
|
||||
val = val >> 3;
|
||||
return sprintf(buf, "%d\n", 3932160 * 2 / (val > 0 ? val : 1));
|
||||
}
|
||||
|
||||
static ssize_t set_fan(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long hsb, lsb;
|
||||
unsigned long tech;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 23000)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val <= 960)
|
||||
{
|
||||
hsb = 0xff; /*high bit*/
|
||||
lsb = 0xe0; /*low bit*/
|
||||
}
|
||||
else
|
||||
{
|
||||
tech = (3932160 * 2) / (val > 0 ? val : 1);
|
||||
hsb = (uint8_t)(((tech << 3) >> 8) & 0x0ff);
|
||||
lsb = (uint8_t)((tech << 3) & 0x0f8);
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_H_RPM(attr->index), hsb);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_L_RPM(attr->index), lsb);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
EMC2305_REG_FAN_DRIVE(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 255)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client,
|
||||
EMC2305_REG_FAN_DRIVE(attr->index),
|
||||
val);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int emc2305_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
int vendor, device;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR);
|
||||
if (vendor != EMC2305_VENDOR)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
|
||||
if (device != EMC2305_DEVICE)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(info->type, "emc2305", I2C_NAME_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc2305_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct emc2305_data *data;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct emc2305_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
dev_info(&client->dev, "%s chip found\n", client->name);
|
||||
|
||||
data->attrs.attrs = emc2305_attr;
|
||||
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
|
||||
if (err)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev))
|
||||
{
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
/* set minimum drive to 0% */
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i), FAN_MINIMUN);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_CONF(i), FAN_RPM_BASED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &data->attrs);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int emc2305_remove(struct i2c_client *client)
|
||||
{
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &data->attrs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_i2c_driver(emc2305_driver);
|
||||
|
||||
MODULE_AUTHOR("Neal Tai<neal.tai@deltaww.com>");
|
||||
MODULE_DESCRIPTION("SMSC EMC2305 fan controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -0,0 +1 @@
|
||||
../../common/modules/dni_emc2305.c
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* <bsn.cl fy=2013 v=gpl>
|
||||
*
|
||||
* Copyright (C) 2017 Delta Networks, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it
|
||||
* and/or modify it under the terms ofthe GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the License,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* </bsn.cl>
|
||||
*
|
||||
*
|
||||
* A hwmon driver for the SMSC EMC2305 fan controller
|
||||
* Complete datasheet is available (6/2013) at:
|
||||
* http://www.smsc.com/media/Downloads_Public/Data_Sheets/2305.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
|
||||
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf);
|
||||
static ssize_t set_fan(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf);
|
||||
static ssize_t set_fan_percentage(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t show_fan_percentage(struct device *dev, struct device_attribute * devattr,
|
||||
char *buf);
|
||||
static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, 0x2F, 0x4C,
|
||||
0x4D, I2C_CLIENT_END
|
||||
};
|
||||
|
||||
|
||||
#define EMC2305_REG_DEVICE 0xFD
|
||||
#define EMC2305_REG_VENDOR 0xFE
|
||||
|
||||
//#define FAN_MINIMUN 0x33 /*20%*/
|
||||
#define FAN_MINIMUN 0x0 /*0%*/
|
||||
#define FAN_RPM_BASED 0xAB
|
||||
|
||||
#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_TACH(n) (0x3E + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_CONF(n) (0x32 + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_REAR_H_RPM(n) (0x3D + 0x10 * n)
|
||||
#define EMC2305_REG_FAN_REAR_L_RPM(n) (0x3C + 0x10 * n)
|
||||
|
||||
#define EMC2305_DEVICE 0x34
|
||||
#define EMC2305_VENDOR 0x5D
|
||||
#define MAX_FAN_SPEED 23000
|
||||
|
||||
struct emc2305_data
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
struct attribute_group attrs;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int emc2305_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int emc2305_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info);
|
||||
static int emc2305_remove(struct i2c_client *client);
|
||||
|
||||
static const struct i2c_device_id emc2305_id[] =
|
||||
{
|
||||
{ "emc2305", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc2305_id);
|
||||
|
||||
static struct i2c_driver emc2305_driver =
|
||||
{
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "emc2305",
|
||||
},
|
||||
.probe = emc2305_probe,
|
||||
.remove = emc2305_remove,
|
||||
.id_table = emc2305_id,
|
||||
.detect = emc2305_detect,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_input, S_IWUSR | S_IRUGO, show_fan, set_fan, 4);
|
||||
static SENSOR_DEVICE_ATTR(fan1_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 2);
|
||||
static SENSOR_DEVICE_ATTR(fan4_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 3);
|
||||
static SENSOR_DEVICE_ATTR(fan5_input_percentage, S_IWUSR | S_IRUGO, show_fan_percentage, set_fan_percentage, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 4);
|
||||
|
||||
static struct attribute *emc2305_attr[] =
|
||||
{
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_input_percentage.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm4.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static ssize_t show_fan_percentage(struct device *dev, struct device_attribute * devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_word_swapped(client,
|
||||
EMC2305_REG_FAN_TACH(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
/* Left shift 3 bits for showing correct RPM */
|
||||
val = val >> 3;
|
||||
if ((int)(3932160 * 2 / (val > 0 ? val : 1) == 960))return sprintf(buf, "%d\n", 0);
|
||||
return sprintf(buf, "%d\n", (int)(3932160 * 2 / (val > 0 ? val : 1) * 100 / MAX_FAN_SPEED));
|
||||
}
|
||||
|
||||
|
||||
static ssize_t set_fan_percentage(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long hsb, lsb;
|
||||
unsigned long tech;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 100)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val <= 5)
|
||||
{
|
||||
hsb = 0xff; /*high bit*/
|
||||
lsb = 0xe0; /*low bit*/
|
||||
}
|
||||
else
|
||||
{
|
||||
val = val * 230;
|
||||
tech = (3932160 * 2) / (val > 0 ? val : 1);
|
||||
hsb = (uint8_t)(((tech << 3) >> 8) & 0x0ff);
|
||||
lsb = (uint8_t)((tech << 3) & 0x0f8);
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_H_RPM(attr->index), hsb);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_L_RPM(attr->index), lsb);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_word_swapped(client,
|
||||
EMC2305_REG_FAN_TACH(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
/* Left shift 3 bits for showing correct RPM */
|
||||
val = val >> 3;
|
||||
return sprintf(buf, "%d\n", 3932160 * 2 / (val > 0 ? val : 1));
|
||||
}
|
||||
|
||||
static ssize_t set_fan(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long hsb, lsb;
|
||||
unsigned long tech;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 23000)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val <= 960)
|
||||
{
|
||||
hsb = 0xff; /*high bit*/
|
||||
lsb = 0xe0; /*low bit*/
|
||||
}
|
||||
else
|
||||
{
|
||||
tech = (3932160 * 2) / (val > 0 ? val : 1);
|
||||
hsb = (uint8_t)(((tech << 3) >> 8) & 0x0ff);
|
||||
lsb = (uint8_t)((tech << 3) & 0x0f8);
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_H_RPM(attr->index), hsb);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_REAR_L_RPM(attr->index), lsb);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
EMC2305_REG_FAN_DRIVE(attr->index));
|
||||
mutex_unlock(&data->lock);
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
if (val > 255)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
i2c_smbus_write_byte_data(client,
|
||||
EMC2305_REG_FAN_DRIVE(attr->index),
|
||||
val);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int emc2305_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
int vendor, device;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR);
|
||||
if (vendor != EMC2305_VENDOR)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
|
||||
if (device != EMC2305_DEVICE)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(info->type, "emc2305", I2C_NAME_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc2305_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct emc2305_data *data;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct emc2305_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
dev_info(&client->dev, "%s chip found\n", client->name);
|
||||
|
||||
data->attrs.attrs = emc2305_attr;
|
||||
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
|
||||
if (err)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev))
|
||||
{
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
/* set minimum drive to 0% */
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i), FAN_MINIMUN);
|
||||
i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_CONF(i), FAN_RPM_BASED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &data->attrs);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int emc2305_remove(struct i2c_client *client)
|
||||
{
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &data->attrs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_i2c_driver(emc2305_driver);
|
||||
|
||||
MODULE_AUTHOR("Neal Tai<neal.tai@deltaww.com>");
|
||||
MODULE_DESCRIPTION("SMSC EMC2305 fan controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -14,3 +14,8 @@ Package: platform-modules-ag9064
|
||||
Architecture: amd64
|
||||
Depends: linux-image-3.16.0-5-amd64
|
||||
Description: kernel modules for platform devices such as fan, led, sfp
|
||||
|
||||
Package: platform-modules-ag5648
|
||||
Architecture: amd64
|
||||
Depends: linux-image-3.16.0-5-amd64
|
||||
Description: kernel modules for platform devices such as fan, led, sfp
|
||||
|
@ -1,2 +0,0 @@
|
||||
platform-modules-ag9032v1_1.1_amd64.deb main extra
|
||||
platform-modules-ag9064_1.1_amd64.deb main extra
|
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: setup-board
|
||||
# Required-Start:
|
||||
# Required-Stop:
|
||||
# Should-Start:
|
||||
# Should-Stop:
|
||||
# Default-Start: S
|
||||
# Default-Stop: 0 6
|
||||
# Short-Description: Setup ag5648 board.
|
||||
### END INIT INFO
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Setting up board... "
|
||||
depmod -a
|
||||
rmmod i2c-i801
|
||||
rmmod i2c-ismt
|
||||
modprobe i2c-dev
|
||||
modprobe i2c-i801
|
||||
modprobe i2c-ismt
|
||||
modprobe i2c-mux-pca954x
|
||||
modprobe dni_ag5648_psu
|
||||
modprobe dni_emc2305
|
||||
modprobe at24
|
||||
modprobe delta_ag5648_platform
|
||||
|
||||
/usr/local/bin/ag5648_platform_init.sh
|
||||
|
||||
echo "done."
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo "done."
|
||||
|
||||
;;
|
||||
|
||||
force-reload|restart)
|
||||
echo "Not supported"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: /etc/init.d/platform-modules-ag5648.init {start|stop}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
@ -0,0 +1,3 @@
|
||||
ag5648/scripts/ag5648_platform_init.sh usr/local/bin
|
||||
ag5648/cfg/ag5648-modules.conf etc/modules-load.d
|
||||
ag5648/scripts/led_control usr/local/bin
|
@ -5,7 +5,7 @@ export INSTALL_MOD_DIR:=extra
|
||||
KVERSION ?= $(shell uname -r)
|
||||
KERNEL_SRC := /lib/modules/$(KVERSION)
|
||||
MOD_SRC_DIR:= $(shell pwd)
|
||||
MODULE_DIRS:= ag9032v1 ag9064
|
||||
MODULE_DIRS:= ag9032v1 ag9064 ag5648
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
Loading…
Reference in New Issue
Block a user