2019-05-09 02:57:17 -05:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
#############################################################################
|
|
|
|
# Celestica
|
|
|
|
#
|
|
|
|
# Module contains an implementation of SONiC Platform Base API and
|
|
|
|
# provides the fan status which are available in the platform
|
|
|
|
#
|
|
|
|
#############################################################################
|
|
|
|
|
|
|
|
import json
|
|
|
|
import math
|
|
|
|
import os.path
|
|
|
|
|
|
|
|
try:
|
|
|
|
from sonic_platform_base.fan_base import FanBase
|
|
|
|
except ImportError as e:
|
|
|
|
raise ImportError(str(e) + "- required module not found")
|
|
|
|
|
|
|
|
CONFIG_DB_PATH = "/etc/sonic/config_db.json"
|
|
|
|
EMC2305_PATH = "/sys/bus/i2c/drivers/emc2305/"
|
|
|
|
SYS_GPIO_DIR = "/sys/class/gpio"
|
|
|
|
EMC2305_MAX_PWM = 255
|
|
|
|
EMC2305_FAN_PWM = "pwm{}"
|
|
|
|
EMC2305_FAN_TARGET = "fan{}_target"
|
|
|
|
EMC2305_FAN_INPUT = "pwm{}"
|
2019-07-23 12:00:02 -05:00
|
|
|
FAN_NAME_LIST = ["FAN-1", "FAN-2", "FAN-3", "FAN-4", "FAN-5"]
|
2019-05-09 02:57:17 -05:00
|
|
|
|
|
|
|
|
|
|
|
class Fan(FanBase):
|
|
|
|
"""Platform-specific Fan class"""
|
|
|
|
|
|
|
|
def __init__(self, fan_index):
|
|
|
|
self.index = fan_index
|
|
|
|
self.config_data = {}
|
|
|
|
self.fan_speed = 0
|
|
|
|
FanBase.__init__(self)
|
|
|
|
|
|
|
|
# dx010 fan attributes
|
|
|
|
# Two EMC2305s located at i2c-13-4d and i2c-13-2e
|
|
|
|
# to control a dual-fan module.
|
|
|
|
self.dx010_emc2305_chip = [
|
|
|
|
{
|
|
|
|
'device': "13-002e",
|
|
|
|
'index_map': [2, 1, 4, 5, 3]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'device': "13-004d",
|
|
|
|
'index_map': [2, 4, 5, 3, 1]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
self.dx010_fan_gpio = [
|
|
|
|
{'base': self.get_gpio_base()},
|
|
|
|
{'prs': 10, 'dir': 15, 'color': {'red': 31, 'green': 32}},
|
|
|
|
{'prs': 11, 'dir': 16, 'color': {'red': 29, 'green': 30}},
|
|
|
|
{'prs': 12, 'dir': 17, 'color': {'red': 35, 'green': 36}},
|
|
|
|
{'prs': 13, 'dir': 18, 'color': {'red': 37, 'green': 38}},
|
|
|
|
{'prs': 14, 'dir': 19, 'color': {'red': 33, 'green': 34}},
|
|
|
|
]
|
|
|
|
|
|
|
|
def get_gpio_base(self):
|
|
|
|
for r in os.listdir(SYS_GPIO_DIR):
|
|
|
|
if "gpiochip" in r:
|
|
|
|
return int(r[8:], 10)
|
|
|
|
return 216 # Reserve
|
|
|
|
|
|
|
|
def get_gpio_value(self, pinnum):
|
|
|
|
gpio_base = self.dx010_fan_gpio[0]['base']
|
|
|
|
|
|
|
|
gpio_dir = SYS_GPIO_DIR + '/gpio' + str(gpio_base+pinnum)
|
|
|
|
gpio_file = gpio_dir + "/value"
|
|
|
|
|
|
|
|
try:
|
|
|
|
with open(gpio_file, 'r') as fd:
|
|
|
|
retval = fd.read()
|
|
|
|
except IOError:
|
|
|
|
raise IOError("Unable to open " + gpio_file + "file !")
|
|
|
|
|
|
|
|
retval = retval.rstrip('\r\n')
|
|
|
|
return retval
|
|
|
|
|
|
|
|
def set_gpio_value(self, pinnum, value=0):
|
|
|
|
gpio_base = self.dx010_fan_gpio[0]['base']
|
|
|
|
|
|
|
|
gpio_dir = SYS_GPIO_DIR + '/gpio' + str(gpio_base+pinnum)
|
|
|
|
gpio_file = gpio_dir + "/value"
|
|
|
|
|
|
|
|
try:
|
|
|
|
with open(gpio_file, 'w') as fd:
|
|
|
|
retval = fd.write(str(value))
|
|
|
|
except IOError:
|
|
|
|
raise IOError("Unable to open " + gpio_file + "file !")
|
|
|
|
|
|
|
|
def get_direction(self):
|
|
|
|
|
|
|
|
direction = self.FAN_DIRECTION_INTAKE
|
|
|
|
raw = self.get_gpio_value(self.dx010_fan_gpio[self.index+1]['dir'])
|
|
|
|
|
|
|
|
if int(raw, 10) == 0:
|
|
|
|
direction = self.FAN_DIRECTION_INTAKE
|
|
|
|
else:
|
|
|
|
direction = self.FAN_DIRECTION_EXHAUST
|
|
|
|
|
|
|
|
return direction
|
|
|
|
|
|
|
|
def get_speed(self):
|
|
|
|
"""
|
|
|
|
DX010 platform specific data:
|
|
|
|
|
|
|
|
speed = pwm_in/255*100
|
|
|
|
"""
|
|
|
|
# TODO: Seperate PSU's fan and main fan class
|
|
|
|
if self.fan_speed != 0:
|
|
|
|
return self.fan_speed
|
|
|
|
else:
|
|
|
|
speed = 0
|
|
|
|
pwm = []
|
|
|
|
emc2305_chips = self.dx010_emc2305_chip
|
|
|
|
|
|
|
|
for chip in emc2305_chips:
|
|
|
|
device = chip['device']
|
|
|
|
fan_index = chip['index_map']
|
|
|
|
sysfs_path = "%s%s/%s" % (
|
|
|
|
EMC2305_PATH, device, EMC2305_FAN_INPUT)
|
|
|
|
sysfs_path = sysfs_path.format(fan_index[self.index])
|
|
|
|
try:
|
|
|
|
with open(sysfs_path, 'r') as file:
|
|
|
|
raw = file.read().strip('\r\n')
|
|
|
|
pwm.append(int(raw, 10))
|
|
|
|
except IOError:
|
|
|
|
raise IOError("Unable to open " + sysfs_path)
|
|
|
|
|
|
|
|
speed = math.ceil(
|
|
|
|
float(pwm[0]) * 100 / EMC2305_MAX_PWM)
|
|
|
|
|
|
|
|
return int(speed)
|
|
|
|
|
|
|
|
def get_target_speed(self):
|
|
|
|
"""
|
|
|
|
DX010 platform specific data:
|
|
|
|
|
|
|
|
speed_pc = pwm_target/255*100
|
|
|
|
|
|
|
|
0 : when PWM mode is use
|
|
|
|
pwm : when pwm mode is not use
|
|
|
|
|
|
|
|
"""
|
|
|
|
target = 0
|
|
|
|
pwm = []
|
|
|
|
emc2305_chips = self.dx010_emc2305_chip
|
|
|
|
|
|
|
|
for chip in emc2305_chips:
|
|
|
|
device = chip['device']
|
|
|
|
fan_index = chip['index_map']
|
|
|
|
sysfs_path = "%s%s/%s" % (
|
|
|
|
EMC2305_PATH, device, EMC2305_FAN_TARGET)
|
|
|
|
sysfs_path = sysfs_path.format(fan_index[self.index])
|
|
|
|
try:
|
|
|
|
with open(sysfs_path, 'r') as file:
|
|
|
|
raw = file.read().strip('\r\n')
|
|
|
|
pwm.append(int(raw, 10))
|
|
|
|
except IOError:
|
|
|
|
raise IOError("Unable to open " + sysfs_path)
|
|
|
|
|
|
|
|
target = pwm[0] * 100 / EMC2305_MAX_PWM
|
|
|
|
|
|
|
|
return target
|
|
|
|
|
|
|
|
def get_speed_tolerance(self):
|
|
|
|
"""
|
|
|
|
Retrieves the speed tolerance of the fan
|
|
|
|
Returns:
|
|
|
|
An integer, the percentage of variance from target speed which is
|
|
|
|
considered tolerable
|
|
|
|
"""
|
|
|
|
return 10
|
|
|
|
|
|
|
|
def set_speed(self, speed):
|
|
|
|
"""
|
|
|
|
Depends on pwm or target mode is selected:
|
|
|
|
1) pwm = speed_pc * 255 <-- Currently use this mode.
|
|
|
|
2) target_pwm = speed_pc * 100 / 255
|
|
|
|
2.1) set pwm{}_enable to 3
|
|
|
|
|
|
|
|
"""
|
|
|
|
pwm = speed * 255 / 100
|
|
|
|
emc2305_chips = self.dx010_emc2305_chip
|
|
|
|
|
|
|
|
for chip in emc2305_chips:
|
|
|
|
device = chip['device']
|
|
|
|
fan_index = chip['index_map']
|
|
|
|
sysfs_path = "%s%s/%s" % (
|
|
|
|
EMC2305_PATH, device, EMC2305_FAN_PWM)
|
|
|
|
sysfs_path = sysfs_path.format(fan_index[self.index])
|
|
|
|
try:
|
|
|
|
with open(sysfs_path, 'w') as file:
|
|
|
|
file.write(str(int(pwm)))
|
|
|
|
except IOError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def set_status_led(self, color):
|
|
|
|
try:
|
|
|
|
if color == self.STATUS_LED_COLOR_GREEN:
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['red'], 1)
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['green'], 0)
|
|
|
|
|
|
|
|
elif color == self.STATUS_LED_COLOR_RED:
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['red'], 0)
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['green'], 1)
|
|
|
|
|
|
|
|
elif color == self.STATUS_LED_COLOR_OFF:
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['red'], 1)
|
|
|
|
self.set_gpio_value(
|
|
|
|
self.dx010_fan_gpio[self.index+1]['color']['green'], 1)
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
except IOError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
2019-07-23 12:00:02 -05:00
|
|
|
|
|
|
|
def get_name(self):
|
|
|
|
"""
|
|
|
|
Retrieves the name of the device
|
|
|
|
Returns:
|
|
|
|
string: The name of the device
|
|
|
|
"""
|
|
|
|
return FAN_NAME_LIST[self.index]
|
|
|
|
|
|
|
|
def get_presence(self):
|
|
|
|
"""
|
|
|
|
Retrieves the presence of the PSU
|
|
|
|
Returns:
|
|
|
|
bool: True if PSU is present, False if not
|
|
|
|
"""
|
|
|
|
raw = self.get_gpio_value(self.dx010_fan_gpio[self.index+1]['prs'])
|
|
|
|
|
|
|
|
return int(raw, 10) == 0
|